From ba1801fc598f43f4f33127139689fada684efa8b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 16 Apr 2023 20:47:31 +0200 Subject: [PATCH 001/240] Add EnergyBroker usecase actor --- spine/model/usecaseinformation.go | 1 + 1 file changed, 1 insertion(+) diff --git a/spine/model/usecaseinformation.go b/spine/model/usecaseinformation.go index 8e531fb2..d65ec1e6 100644 --- a/spine/model/usecaseinformation.go +++ b/spine/model/usecaseinformation.go @@ -10,6 +10,7 @@ const ( UseCaseActorTypeControllableSystem UseCaseActorType = "ControllableSystem" UseCaseActorTypeDHWCircuit UseCaseActorType = "DHWCircuit" UseCaseActorTypeEnergyGuard UseCaseActorType = "EnergyGuard" + UseCaseActorTypeEnergyBroker UseCaseActorType = "EnergyBroker" UseCaseActorTypeEVSE UseCaseActorType = "EVSE" UseCaseActorTypeEV UseCaseActorType = "EV" UseCaseActorTypeGridConnectionPoint UseCaseActorType = "GridConnectionPoint" From 02fa99a8d28e6d31da7796884c2666f2d2cb0cae Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 16 Apr 2023 21:07:49 +0200 Subject: [PATCH 002/240] Add more missing usecase actors --- spine/model/usecaseinformation.go | 43 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/spine/model/usecaseinformation.go b/spine/model/usecaseinformation.go index d65ec1e6..0426479e 100644 --- a/spine/model/usecaseinformation.go +++ b/spine/model/usecaseinformation.go @@ -3,25 +3,30 @@ package model type UseCaseActorType string const ( - UseCaseActorTypeBatterySystem UseCaseActorType = "BatterySystem" - UseCaseActorTypeCEM UseCaseActorType = "CEM" - UseCaseActorTypeConfigurationAppliance UseCaseActorType = "ConfigurationAppliance" - UseCaseActorTypeCompressor UseCaseActorType = "Compressor" - UseCaseActorTypeControllableSystem UseCaseActorType = "ControllableSystem" - UseCaseActorTypeDHWCircuit UseCaseActorType = "DHWCircuit" - UseCaseActorTypeEnergyGuard UseCaseActorType = "EnergyGuard" - UseCaseActorTypeEnergyBroker UseCaseActorType = "EnergyBroker" - UseCaseActorTypeEVSE UseCaseActorType = "EVSE" - UseCaseActorTypeEV UseCaseActorType = "EV" - UseCaseActorTypeGridConnectionPoint UseCaseActorType = "GridConnectionPoint" - UseCaseActorTypeHeatPump UseCaseActorType = "HeatPump" - UseCaseActorTypeHeatingCircuit UseCaseActorType = "HeatingCircuit" - UseCaseActorTypeHeatingZone UseCaseActorType = "HeatingZone" - UseCaseActorTypeHVACRoom UseCaseActorType = "HVACRoom" - UseCaseActorTypeMonitoredUnit UseCaseActorType = "MonitoredUnit" - UseCaseActorTypeMonitoringAppliance UseCaseActorType = "MonitoringAppliance" - UseCaseActorTypePVSystem UseCaseActorType = "PVSystem" - UseCaseActorTypeVisualizationAppliance UseCaseActorType = "VisualizationAppliance" + UseCaseActorTypeBattery UseCaseActorType = "Battery" + UseCaseActorTypeBatterySystem UseCaseActorType = "BatterySystem" + UseCaseActorTypeCEM UseCaseActorType = "CEM" + UseCaseActorTypeConfigurationAppliance UseCaseActorType = "ConfigurationAppliance" + UseCaseActorTypeCompressor UseCaseActorType = "Compressor" + UseCaseActorTypeControllableSystem UseCaseActorType = "ControllableSystem" + UseCaseActorTypeDHWCircuit UseCaseActorType = "DHWCircuit" + UseCaseActorTypeEnergyBroker UseCaseActorType = "EnergyBroker" + UseCaseActorTypeEnergyConsumer UseCaseActorType = "EnergyConsumer" + UseCaseActorTypeEnergyGuard UseCaseActorType = "EnergyGuard" + UseCaseActorTypeEVSE UseCaseActorType = "EVSE" + UseCaseActorTypeEV UseCaseActorType = "EV" + UseCaseActorTypeGridConnectionPoint UseCaseActorType = "GridConnectionPoint" + UseCaseActorTypeHeatPump UseCaseActorType = "HeatPump" + UseCaseActorTypeHeatingCircuit UseCaseActorType = "HeatingCircuit" + UseCaseActorTypeHeatingZone UseCaseActorType = "HeatingZone" + UseCaseActorTypeHVACRoom UseCaseActorType = "HVACRoom" + UseCaseActorTypeInverter UseCaseActorType = "Inverter" + UseCaseActorTypeMonitoredUnit UseCaseActorType = "MonitoredUnit" + UseCaseActorTypeMonitoringAppliance UseCaseActorType = "MonitoringAppliance" + UseCaseActorTypeOutdoorTemperatureSensor UseCaseActorType = "OutdoorTemperatureSensor" + UseCaseActorTypePVString UseCaseActorType = "PVString" + UseCaseActorTypePVSystem UseCaseActorType = "PVSystem" + UseCaseActorTypeVisualizationAppliance UseCaseActorType = "VisualizationAppliance" ) type UseCaseNameType string From 01e51fed3e9a6565331fbaba9fb75b0b9305bd02 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 27 Apr 2023 19:36:11 +0200 Subject: [PATCH 003/240] =?UTF-8?q?Don=E2=80=99t=20delay=20reconnection=20?= =?UTF-8?q?attempts=20for=20too=20long?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/hub.go | 5 +---- service/hub_test.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/service/hub.go b/service/hub.go index 9d573681..d45fae20 100644 --- a/service/hub.go +++ b/service/hub.go @@ -37,10 +37,7 @@ type connectionInitiationDelayTimeRange struct { var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ {min: 0, max: 3}, {min: 3, max: 10}, - {min: 10, max: 30}, - {min: 30, max: 60}, - {min: 60, max: 180}, - {min: 180, max: 360}, + {min: 10, max: 20}, } // interface for reporting data from connectionsHub to the EEBUSService diff --git a/service/hub_test.go b/service/hub_test.go index 4a7f28aa..2aa92df1 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -29,14 +29,14 @@ func (s *HubSuite) SetupSuite() { {0, connectionInitiationDelayTimeRanges[0]}, {1, connectionInitiationDelayTimeRanges[1]}, {2, connectionInitiationDelayTimeRanges[2]}, - {3, connectionInitiationDelayTimeRanges[3]}, - {4, connectionInitiationDelayTimeRanges[4]}, - {5, connectionInitiationDelayTimeRanges[5]}, - {6, connectionInitiationDelayTimeRanges[5]}, - {7, connectionInitiationDelayTimeRanges[5]}, - {8, connectionInitiationDelayTimeRanges[5]}, - {9, connectionInitiationDelayTimeRanges[5]}, - {10, connectionInitiationDelayTimeRanges[5]}, + {3, connectionInitiationDelayTimeRanges[2]}, + {4, connectionInitiationDelayTimeRanges[2]}, + {5, connectionInitiationDelayTimeRanges[2]}, + {6, connectionInitiationDelayTimeRanges[2]}, + {7, connectionInitiationDelayTimeRanges[2]}, + {8, connectionInitiationDelayTimeRanges[2]}, + {9, connectionInitiationDelayTimeRanges[2]}, + {10, connectionInitiationDelayTimeRanges[2]}, } } From ffab92c532936850ed959ab8ea4e2176d3fe2fc8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 May 2023 11:51:06 +0200 Subject: [PATCH 004/240] Only add IP to mDNS entry if it is not found --- service/mdns.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/service/mdns.go b/service/mdns.go index 6b4eef5d..6157c879 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -508,7 +508,21 @@ func (m *mdns) processMdnsEntry(elements map[string]string, name, host string, a entry := m.entries[ski] // we assume only network addresses are added - entry.Addresses = append(entry.Addresses, addresses...) + for _, address := range addresses { + // only add if it is not added yet + isNewElement := true + + for _, item := range entry.Addresses { + if item.String() == address.String() { + isNewElement = false + break + } + } + + if isNewElement { + entry.Addresses = append(entry.Addresses, address) + } + } m.entries[ski] = entry } else if !exists && !remove { From db31183217b1d90fbf47620e20dcd56bc8bfae8e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 May 2023 11:51:27 +0200 Subject: [PATCH 005/240] Add SKI to mDNS entry --- service/mdns.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/mdns.go b/service/mdns.go index 6157c879..72c016ff 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -19,6 +19,7 @@ import ( type MdnsEntry struct { Name string + Ski string Identifier string // mandatory Path string // mandatory Register bool // mandatory @@ -529,6 +530,7 @@ func (m *mdns) processMdnsEntry(elements map[string]string, name, host string, a // new newEntry := MdnsEntry{ Name: name, + Ski: ski, Identifier: identifier, Path: path, Register: register == "true", From 89cbfcc945cad0421acf1ee121fcb6bdb2a76ec8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 May 2023 11:52:57 +0200 Subject: [PATCH 006/240] Add possibility to provide found mDNS entries --- cmd/evse/main.go | 3 +++ cmd/hems/main.go | 3 +++ service/hub.go | 32 ++++++++++++++++++++++++++++++++ service/hub_test.go | 7 ++++--- service/service.go | 17 +++++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 73729d09..06489e38 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -93,6 +93,9 @@ func (h *evse) RemoteSKIConnected(service *service.EEBUSService, ski string) {} func (h *evse) RemoteSKIDisconnected(service *service.EEBUSService, ski string) {} +func (h *evse) VisibleMDNSRecordsUpdated(service *service.EEBUSService, entries []service.MdnsEntry) { +} + func (h *evse) ReportServiceShipID(ski string, shipdID string) {} // main app diff --git a/cmd/hems/main.go b/cmd/hems/main.go index 2f61f1e9..33a00125 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -93,6 +93,9 @@ func (h *hems) RemoteSKIConnected(service *service.EEBUSService, ski string) {} func (h *hems) RemoteSKIDisconnected(service *service.EEBUSService, ski string) {} +func (h *hems) VisibleMDNSRecordsUpdated(service *service.EEBUSService, entries []service.MdnsEntry) { +} + func (h *hems) ReportServiceShipID(ski string, shipdID string) {} // UCEvseCommisioningConfigurationCemDelegate diff --git a/service/hub.go b/service/hub.go index d45fae20..a7d40fdc 100644 --- a/service/hub.go +++ b/service/hub.go @@ -9,7 +9,9 @@ import ( "math/rand" "net" "net/http" + "sort" "strconv" + "strings" "sync" "time" @@ -42,6 +44,9 @@ var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ // interface for reporting data from connectionsHub to the EEBUSService type serviceProvider interface { + // report a newly discovered remote EEBUS service + VisibleMDNSRecordsUpdated(entries []MdnsEntry) + // report a connection to a SKI RemoteSKIConnected(ski string) @@ -75,6 +80,9 @@ type connectionsHub struct { // Handling mDNS related tasks mdns MdnsService + // list of currently known/reported mDNS entries + knownMdnsEntries []MdnsEntry + // the SPINE local device spineLocalDevice *spine.DeviceLocalImpl @@ -90,6 +98,7 @@ func newConnectionsHub(serviceProvider serviceProvider, mdns MdnsService, spineL connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), pairedServices: make([]*ServiceDetails, 0), + knownMdnsEntries: make([]MdnsEntry, 0), serviceProvider: serviceProvider, spineLocalDevice: spineLocalDevice, configuration: configuration, @@ -173,6 +182,16 @@ func (h *connectionsHub) checkRestartMdnsSearch() { } } +func (h *connectionsHub) StartBrowseMdnsSearch() { + // TODO: this currently collides with searching for a specific SKI + h.mdns.RegisterMdnsSearch(h) +} + +func (h *connectionsHub) StopBrowseMdnsSearch() { + // TODO: this currently collides with searching for a specific SKI + h.mdns.UnregisterMdnsSearch(h) +} + // Provides the SHIP ID the remote service reported during the handshake process func (h *connectionsHub) ReportServiceShipID(ski string, shipdID string) { h.serviceProvider.RemoteSKIConnected(ski) @@ -517,7 +536,11 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]MdnsEntry) { h.muxMdns.Lock() defer h.muxMdns.Unlock() + var mdnsEntries []MdnsEntry + for ski, entry := range entries { + mdnsEntries = append(mdnsEntries, entry) + // check if this ski is already connected if h.isSkiConnected(ski) { continue @@ -538,6 +561,15 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]MdnsEntry) { h.coordinateConnectionInitations(ski, entry) } + + sort.Slice(mdnsEntries, func(i, j int) bool { + item1 := mdnsEntries[i] + item2 := mdnsEntries[j] + a := strings.ToLower(item1.Brand + item1.Model + item1.Ski) + b := strings.ToLower(item2.Brand + item2.Model + item2.Ski) + return a < b + }) + h.serviceProvider.VisibleMDNSRecordsUpdated(mdnsEntries) } // coordinate connection initiation attempts to a remove service diff --git a/service/hub_test.go b/service/hub_test.go index 2aa92df1..7958850d 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -43,9 +43,10 @@ func (s *HubSuite) SetupSuite() { // Service Provider Interface var _ serviceProvider = (*HubSuite)(nil) -func (s *HubSuite) RemoteSKIConnected(string) {} -func (s *HubSuite) RemoteSKIDisconnected(string) {} -func (s *HubSuite) ReportServiceShipID(string, string) {} +func (s *HubSuite) VisibleMDNSRecordsUpdated([]MdnsEntry) {} +func (s *HubSuite) RemoteSKIConnected(string) {} +func (s *HubSuite) RemoteSKIDisconnected(string) {} +func (s *HubSuite) ReportServiceShipID(string, string) {} var _ MdnsService = (*HubSuite)(nil) diff --git a/service/service.go b/service/service.go index 4dc4914d..3fafabc8 100644 --- a/service/service.go +++ b/service/service.go @@ -14,6 +14,9 @@ import ( type EEBUSServiceHandler interface { // RemoteServicesListUpdated(services []ServiceDetails) + // report all currently visible EEBUS services + VisibleMDNSRecordsUpdated(service *EEBUSService, entries []MdnsEntry) + // report a connection to a SKI RemoteSKIConnected(service *EEBUSService, ski string) @@ -55,6 +58,10 @@ func NewEEBUSService(configuration *Configuration, serviceHandler EEBUSServiceHa var _ serviceProvider = (*EEBUSService)(nil) +func (s *EEBUSService) VisibleMDNSRecordsUpdated(entries []MdnsEntry) { + s.serviceHandler.VisibleMDNSRecordsUpdated(s, entries) +} + // report a connection to a SKI func (s *EEBUSService) RemoteSKIConnected(ski string) { s.serviceHandler.RemoteSKIConnected(s, ski) @@ -70,6 +77,16 @@ func (s *EEBUSService) ReportServiceShipID(ski string, shipdID string) { s.serviceHandler.ReportServiceShipID(ski, shipdID) } +// Starts browsing for any EEBUS mDNS entry +func (s *EEBUSService) StartBrowseMdnsEntries() { + s.connectionsHub.StartBrowseMdnsSearch() +} + +// Stop brwosing for any EEBUS mDNS entry +func (s *EEBUSService) StopBrowseMdnsEntries() { + s.connectionsHub.StopBrowseMdnsSearch() +} + // Sets a custom logging implementation // By default NoLogging is used, so no logs are printed func (s *EEBUSService) SetLogging(logger logging.Logging) { From 02643c64f816fe9a2c4fdf37398c9c7971db9dbf Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 21 May 2023 20:49:06 +0200 Subject: [PATCH 007/240] Add support for UI based pairing - lots of refactoring - add support for handling pairing via a user interface of some sort - new configuration `SetAutoRetryShipHandshake` added which should be used when there is no pairing UI and auto register is true --- cmd/evse/main.go | 11 +- cmd/hems/main.go | 11 +- go.mod | 1 + go.sum | 20 +-- service/hub.go | 314 +++++++++++++++++++++++++---------- service/hub_test.go | 45 ++--- service/mdns.go | 11 +- service/mock_hub.go | 108 ++++++++++++ service/mock_mdns.go | 145 ++++++++++++++++ service/mocks/MdnsSearch.go | 33 ++++ service/mocks/MdnsService.go | 76 +++++++++ service/service.go | 92 +++++++--- service/types.go | 97 ++++++----- ship/connection.go | 69 ++++++-- ship/connection_test.go | 37 ++--- ship/handshake.go | 111 ++++++++----- ship/hs_access.go | 4 +- ship/hs_access_test.go | 16 +- ship/hs_hello.go | 53 ++++-- ship/hs_hello_client_test.go | 10 +- ship/hs_hello_test.go | 74 ++++----- ship/hs_helper_test.go | 14 +- ship/hs_init.go | 16 +- ship/hs_init_client_test.go | 22 +-- ship/hs_init_server_test.go | 18 +- ship/hs_pin.go | 6 +- ship/hs_pin_test.go | 20 +-- ship/hs_prot.go | 16 +- ship/hs_prot_client_test.go | 8 +- ship/hs_prot_server_test.go | 12 +- ship/mock_types.go | 221 ++++++++++++++++++++++++ ship/types.go | 95 ++++++----- ship/websocket.go | 52 ++++-- spine/mocks/Sender.go | 30 +++- 34 files changed, 1389 insertions(+), 479 deletions(-) create mode 100644 service/mock_hub.go create mode 100644 service/mock_mdns.go create mode 100644 service/mocks/MdnsSearch.go create mode 100644 service/mocks/MdnsService.go create mode 100644 ship/mock_types.go diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 06489e38..dbe08254 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -83,8 +83,7 @@ func (h *evse) run() { h.myService.Start() // defer h.myService.Shutdown() - remoteService := service.NewServiceDetails(remoteSki) - h.myService.PairRemoteService(remoteService) + h.myService.EnablePairingForSKI(remoteSki, true) } // EEBUSServiceHandler @@ -93,10 +92,14 @@ func (h *evse) RemoteSKIConnected(service *service.EEBUSService, ski string) {} func (h *evse) RemoteSKIDisconnected(service *service.EEBUSService, ski string) {} -func (h *evse) VisibleMDNSRecordsUpdated(service *service.EEBUSService, entries []service.MdnsEntry) { +func (h *evse) VisibleRemoteServicesUpdated(service *service.EEBUSService, entries []service.RemoteService) { } -func (h *evse) ReportServiceShipID(ski string, shipdID string) {} +func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} + +func (h *evse) ServicePairingDetailUpdate(ski string, detail service.PairingDetail) {} + +func (h *evse) AllowWaitingForTrust(ski string) bool { return true } // main app func usage() { diff --git a/cmd/hems/main.go b/cmd/hems/main.go index 33a00125..d20074f3 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -83,8 +83,7 @@ func (h *hems) run() { h.myService.Start() // defer h.myService.Shutdown() - remoteService := service.NewServiceDetails(remoteSki) - h.myService.PairRemoteService(remoteService) + h.myService.EnablePairingForSKI(remoteSki, true) } // EEBUSServiceHandler @@ -93,10 +92,14 @@ func (h *hems) RemoteSKIConnected(service *service.EEBUSService, ski string) {} func (h *hems) RemoteSKIDisconnected(service *service.EEBUSService, ski string) {} -func (h *hems) VisibleMDNSRecordsUpdated(service *service.EEBUSService, entries []service.MdnsEntry) { +func (h *hems) VisibleRemoteServicesUpdated(service *service.EEBUSService, entries []service.RemoteService) { } -func (h *hems) ReportServiceShipID(ski string, shipdID string) {} +func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} + +func (h *hems) ServicePairingDetailUpdate(ski string, detail service.PairingDetail) {} + +func (h *hems) AllowWaitingForTrust(ski string) bool { return true } // UCEvseCommisioningConfigurationCemDelegate diff --git a/go.mod b/go.mod index 7183fe22..606bb850 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( require ( github.com/ahmetb/go-linq/v3 v3.2.0 github.com/godbus/dbus/v5 v5.1.0 + github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.0 github.com/holoplot/go-avahi v1.0.1 github.com/libp2p/zeroconf/v2 v2.2.0 diff --git a/go.sum b/go.sum index cf72240b..10796d4a 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -15,8 +17,6 @@ github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfx github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= @@ -32,8 +32,6 @@ github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -42,8 +40,6 @@ gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREv golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -51,9 +47,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -67,21 +60,16 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/service/hub.go b/service/hub.go index a7d40fdc..97096fc9 100644 --- a/service/hub.go +++ b/service/hub.go @@ -42,8 +42,10 @@ var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ {min: 10, max: 20}, } +//go:generate mockgen -destination=mock_hub.go -package=service github.com/enbility/eebus-go/service ServiceProvider + // interface for reporting data from connectionsHub to the EEBUSService -type serviceProvider interface { +type ServiceProvider interface { // report a newly discovered remote EEBUS service VisibleMDNSRecordsUpdated(entries []MdnsEntry) @@ -55,7 +57,13 @@ type serviceProvider interface { // provide the SHIP ID received during SHIP handshake process // the ID needs to be stored and then provided for remote services so it can be compared and verified - ReportServiceShipID(string, string) + ServiceShipIDUpdate(ski string, shipID string) + + // provides the current handshake state for a given SKI + ServicePairingDetailUpdate(ski string, detail PairingDetail) + + // return if the user is still able to trust the connection + AllowWaitingForTrust(ski string) bool } // handling all connections to remote services @@ -69,10 +77,10 @@ type connectionsHub struct { configuration *Configuration localService *ServiceDetails - serviceProvider serviceProvider + serviceProvider ServiceProvider - // The list of paired devices - pairedServices []*ServiceDetails + // The list of known remote services + remoteServices map[string]*ServiceDetails // The web server for handling incoming websocket connections httpServer *http.Server @@ -92,12 +100,12 @@ type connectionsHub struct { muxMdns sync.Mutex } -func newConnectionsHub(serviceProvider serviceProvider, mdns MdnsService, spineLocalDevice *spine.DeviceLocalImpl, configuration *Configuration, localService *ServiceDetails) *connectionsHub { +func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice *spine.DeviceLocalImpl, configuration *Configuration, localService *ServiceDetails) *connectionsHub { hub := &connectionsHub{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), - pairedServices: make([]*ServiceDetails, 0), + remoteServices: make(map[string]*ServiceDetails), knownMdnsEntries: make([]MdnsEntry, 0), serviceProvider: serviceProvider, spineLocalDevice: spineLocalDevice, @@ -133,11 +141,9 @@ var _ ship.ShipServiceDataProvider = (*connectionsHub)(nil) // Returns if the provided SKI is from a registered service func (h *connectionsHub) IsRemoteServiceForSKIPaired(ski string) bool { - if _, err := h.PairedServiceForSKI(ski); err != nil { - return false - } + service := h.serviceForSKI(ski) - return true + return service.Paired } // The connection was closed, we need to clean up @@ -159,21 +165,39 @@ func (h *connectionsHub) HandleConnectionClosed(connection *ship.ShipConnection, h.serviceProvider.RemoteSKIDisconnected(connection.RemoteSKI) + // Do not automatically reconnect if handshake failed and autoRetry is false + if !handshakeCompleted && !h.configuration.autoRetryShipHandshake { + return + } + h.checkRestartMdnsSearch() } -// startup mDNS if a paired service is not connected -func (h *connectionsHub) checkRestartMdnsSearch() { +// return the number of paired services +func (h *connectionsHub) numberPairedServices() int { + amount := 0 + h.muxReg.Lock() - countPairedServices := len(h.pairedServices) + for _, service := range h.remoteServices { + if service.Paired { + amount++ + } + } h.muxReg.Unlock() + + return amount +} + +// startup mDNS if a paired service is not connected +func (h *connectionsHub) checkRestartMdnsSearch() { + countPairedServices := h.numberPairedServices() h.muxCon.Lock() countConnections := len(h.connections) h.muxCon.Unlock() if countPairedServices > countConnections { // if this is not a CEM also start the mDNS announcement - if h.localService.deviceType != model.DeviceTypeTypeEnergyManagementSystem { + if h.localService.DeviceType != model.DeviceTypeTypeEnergyManagementSystem { _ = h.mdns.AnnounceMdnsEntry() } @@ -196,7 +220,88 @@ func (h *connectionsHub) StopBrowseMdnsSearch() { func (h *connectionsHub) ReportServiceShipID(ski string, shipdID string) { h.serviceProvider.RemoteSKIConnected(ski) - h.serviceProvider.ReportServiceShipID(ski, shipdID) + h.serviceProvider.ServiceShipIDUpdate(ski, shipdID) +} + +// return if the user is still able to trust the connection +func (h *connectionsHub) AllowWaitingForTrust(ski string) bool { + return h.serviceProvider.AllowWaitingForTrust(ski) +} + +// Provides the current ship message exchange state for a given SKI and the corresponding error if state is error +func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.ShipState) { + service := h.serviceForSKI(ski) + + pairingState := h.mapShipMessageExchangeState(state.State, ski) + if state.Error != nil && state.Error != ErrConnectionNotFound { + pairingState = PairingStateError + } + + pairingDetail := PairingDetail{ + State: pairingState, + Error: state.Error, + } + + existingDetails := service.PairingDetail + if existingDetails.State != pairingState || existingDetails.Error != state.Error { + service.PairingDetail = pairingDetail + + h.serviceProvider.ServicePairingDetailUpdate(ski, pairingDetail) + } +} + +// Provide the current pairing state for a given SKI +// +// returns: +// +// ErrNotPaired if the SKI is not in the (to be) paired list +// ErrNoConnectionFound if no connection for the SKI was found +func (h *connectionsHub) PairingDetailForSki(ski string) PairingDetail { + service := h.serviceForSKI(ski) + + if conn := h.connectionForSKI(ski); conn != nil { + shipState, shipError := conn.ShipHandshakeState() + state := h.mapShipMessageExchangeState(shipState, ski) + return PairingDetail{ + State: state, + Error: shipError, + } + } + + return service.PairingDetail +} + +// maps ShipMessageExchangeState to PairingState +func (h *connectionsHub) mapShipMessageExchangeState(state ship.ShipMessageExchangeState, ski string) PairingState { + var connState PairingState + + // map the SHIP states to a public gState + switch state { + case ship.CmiStateInitStart: + connState = PairingStateQueued + case ship.CmiStateClientSend, ship.CmiStateClientWait, ship.CmiStateClientEvaluate, + ship.CmiStateServerWait, ship.CmiStateServerEvaluate: + connState = PairingStateInitiated + case ship.SmeHelloStateReadyInit, ship.SmeHelloStateReadyListen, ship.SmeHelloStateReadyTimeout: + connState = PairingStateInProgress + case ship.SmeHelloStatePendingInit, ship.SmeHelloStatePendingListen, ship.SmeHelloStatePendingTimeout: + connState = PairingStateReceived + case ship.SmePinStateCheckInit, ship.SmePinStateCheckListen, ship.SmePinStateCheckError, + ship.SmePinStateCheckBusyInit, ship.SmePinStateCheckBusyWait, ship.SmePinStateCheckOk, + ship.SmePinStateAskInit, ship.SmePinStateAskProcess, ship.SmePinStateAskRestricted, + ship.SmePinStateAskOk: + connState = PairingStatePin + case ship.SmeAccessMethodsRequest, ship.SmeApproved: + connState = PairingStateInProgress + case ship.SmeComplete: + connState = PairingStateAccepted + case ship.SmeError: + connState = PairingStateError + default: + connState = PairingStateInProgress + } + + return connState } // Disconnect a connection to an SKI, used by a service implementation @@ -221,7 +326,7 @@ func (h *connectionsHub) registerConnection(connection *ship.ShipConnection) { h.muxCon.Unlock() // shutdown mDNS if this is not a CEM - if h.localService.deviceType != model.DeviceTypeTypeEnergyManagementSystem { + if h.localService.DeviceType != model.DeviceTypeTypeEnergyManagementSystem { h.mdns.UnannounceMdnsEntry() h.mdns.UnregisterMdnsSearch(h) } @@ -320,47 +425,55 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { // check if the client supports the ship sub protocol if conn.Subprotocol() != shipWebsocketSubProtocol { logging.Log.Debug("client does not support the ship sub protocol") - conn.Close() + _ = conn.Close() return } // check if the clients certificate provides a SKI if len(r.TLS.PeerCertificates) == 0 { logging.Log.Debug("client does not provide a certificate") - conn.Close() + _ = conn.Close() return } ski, err := skiFromCertificate(r.TLS.PeerCertificates[0]) if err != nil { logging.Log.Debug(err) - conn.Close() + _ = conn.Close() return } // normalize the incoming SKI remoteService := NewServiceDetails(ski) - logging.Log.Debug("incoming connection request from", remoteService.SKI()) + logging.Log.Debug("incoming connection request from", remoteService.SKI) // Check if the remote service is paired - _, err = h.PairedServiceForSKI(remoteService.SKI()) - if err != nil { - logging.Log.Debug("ski", ski, "is not paired, closing the connection") - return - } + service := h.serviceForSKI(remoteService.SKI) + if service.PairingDetail.State == PairingStateQueued { + // Check if pairing is made possible + if !h.serviceProvider.AllowWaitingForTrust(ski) { + logging.Log.Debug("ski", ski, "is not paired, closing the connection") + msg := "Node rejected by application" + _ = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(4452, msg)) + _ = conn.Close() + return + } - // check if we already know this remote service - if remoteS, err := h.PairedServiceForSKI(remoteService.SKI()); err == nil { - remoteService = remoteS + service.PairingDetail.State = PairingStateReceived + h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) } + remoteService = service + // don't allow a second connection if !h.keepThisConnection(conn, true, remoteService) { + _ = conn.Close() return } - dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI()) - shipConnection := ship.NewConnectionHandler(h, dataHandler, h.spineLocalDevice, ship.ShipRoleServer, h.localService.ShipID(), remoteService.SKI(), remoteService.ShipID()) + dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI) + shipConnection := ship.NewConnectionHandler(h, dataHandler, h.spineLocalDevice, ship.ShipRoleServer, + h.localService.ShipID, remoteService.SKI, remoteService.ShipID, h.configuration.autoRetryShipHandshake) shipConnection.Run() h.registerConnection(shipConnection) @@ -370,11 +483,11 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { // // returns error contains a reason for failing the connection or nil if no further tries should be processed func (h *connectionsHub) connectFoundService(remoteService *ServiceDetails, host, port string) error { - if h.isSkiConnected(remoteService.SKI()) { + if h.isSkiConnected(remoteService.SKI) { return nil } - logging.Log.Debugf("initiating connection to %s at %s:%s", remoteService.SKI(), host, port) + logging.Log.Debugf("initiating connection to %s at %s:%s", remoteService.SKI, host, port) dialer := &websocket.Dialer{ Proxy: http.ProxyFromEnvironment, @@ -398,33 +511,34 @@ func (h *connectionsHub) connectFoundService(remoteService *ServiceDetails, host if len(remoteCerts) == 0 || remoteCerts[0].SubjectKeyId == nil { // Close connection as we couldn't get the remote SKI - errorString := fmt.Sprintf("closing connection to %s: could not get remote SKI from certificate", remoteService.SKI()) + errorString := fmt.Sprintf("closing connection to %s: could not get remote SKI from certificate", remoteService.SKI) conn.Close() return errors.New(errorString) } if _, err := skiFromCertificate(remoteCerts[0]); err != nil { // Close connection as the remote SKI can't be correct - errorString := fmt.Sprintf("closing connection to %s: %s", remoteService.SKI(), err) + errorString := fmt.Sprintf("closing connection to %s: %s", remoteService.SKI, err) conn.Close() return errors.New(errorString) } remoteSKI := fmt.Sprintf("%0x", remoteCerts[0].SubjectKeyId) - if remoteSKI != remoteService.SKI() { - errorString := fmt.Sprintf("closing connection to %s: SKI does not match %s", remoteService.SKI(), remoteSKI) + if remoteSKI != remoteService.SKI { + errorString := fmt.Sprintf("closing connection to %s: SKI does not match %s", remoteService.SKI, remoteSKI) conn.Close() return errors.New(errorString) } if !h.keepThisConnection(conn, false, remoteService) { - errorString := fmt.Sprintf("closing connection to %s: ignoring this connection", remoteService.SKI()) + errorString := fmt.Sprintf("closing connection to %s: ignoring this connection", remoteService.SKI) return errors.New(errorString) } - dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI()) - shipConnection := ship.NewConnectionHandler(h, dataHandler, h.spineLocalDevice, ship.ShipRoleClient, h.localService.ShipID(), remoteService.SKI(), remoteService.ShipID()) + dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI) + shipConnection := ship.NewConnectionHandler(h, dataHandler, h.spineLocalDevice, ship.ShipRoleClient, + h.localService.ShipID, remoteService.SKI, remoteService.ShipID, h.configuration.autoRetryShipHandshake) shipConnection.Run() h.registerConnection(shipConnection) @@ -446,7 +560,7 @@ func (h *connectionsHub) keepThisConnection(conn *websocket.Conn, incomingReques // This is hard to implement without any flaws. Therefor I chose a // different approach: The connection initiated by the higher SKI will be kept - remoteSKI := remoteService.SKI() + remoteSKI := remoteService.SKI existingC := h.connectionForSKI(remoteSKI) if existingC == nil { return true @@ -454,9 +568,9 @@ func (h *connectionsHub) keepThisConnection(conn *websocket.Conn, incomingReques keep := false if incomingRequest { - keep = remoteSKI > h.localService.SKI() + keep = remoteSKI > h.localService.SKI } else { - keep = h.localService.SKI() > remoteSKI + keep = h.localService.SKI > remoteSKI } if keep { @@ -480,55 +594,67 @@ func (h *connectionsHub) keepThisConnection(conn *websocket.Conn, incomingReques return keep } -func (h *connectionsHub) PairedServiceForSKI(ski string) (*ServiceDetails, error) { +// return the service for a given SKI or an error if not found +func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { h.muxReg.Lock() defer h.muxReg.Unlock() - for _, service := range h.pairedServices { - if service.SKI() == ski { - return service, nil - } + service, ok := h.remoteServices[ski] + if !ok { + service = NewServiceDetails(ski) + service.PairingDetail.State = PairingStateNone + h.remoteServices[ski] = service } - return nil, fmt.Errorf("no registered service found for SKI %s", ski) + + return service +} + +// Sets the SKI to be paired +// Should be used if for services which finalized the pairing process and where +// stored as having the process completed +func (h *connectionsHub) EnablePairingForSKI(ski string, enable bool) { + service := h.serviceForSKI(ski) + service.Paired = enable } -// Adds a new device to the list of known devices which can be connected to -// and connect it if it is currently not connected -func (h *connectionsHub) PairRemoteService(service *ServiceDetails) { - // check if it is already registered - if _, err := h.PairedServiceForSKI(service.SKI()); err != nil { - h.muxReg.Lock() - h.pairedServices = append(h.pairedServices, service) - h.muxReg.Unlock() +// Triggers the pairing process for a SKI +func (h *connectionsHub) InitiatePairingWithSKI(ski string) { + conn := h.connectionForSKI(ski) + + // remotely initiated + if conn != nil { + conn.ApprovePendingHandshake() + + return } - if !h.isSkiConnected(service.SKI()) { + // locally initiated + service := h.serviceForSKI(ski) + service.PairingDetail.State = PairingStateQueued + + h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) + + // initiate a search and also a connection if it does not yet exist + if !h.isSkiConnected(service.SKI) { h.mdns.RegisterMdnsSearch(h) } } -// Remove a device from the list of known devices which can be connected to -// and disconnect it if it is currently connected -func (h *connectionsHub) UnpairRemoteService(ski string) error { +// Cancels the pairing process for a SKI +func (h *connectionsHub) CancelPairingWithSKI(ski string) { h.removeConnectionAttemptCounter(ski) - newRegisteredDevice := make([]*ServiceDetails, 0) + service := h.serviceForSKI(ski) + service.PairingDetail.State = PairingStateNone + service.Paired = false - h.muxReg.Lock() - for _, device := range h.pairedServices { - if device.SKI() != ski { - newRegisteredDevice = append(newRegisteredDevice, device) - } - } + h.removeConnectionAttemptCounter(ski) - h.pairedServices = newRegisteredDevice - h.muxReg.Unlock() + h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) if existingC := h.connectionForSKI(ski); existingC != nil { - existingC.CloseConnection(true, "pairing removed") + existingC.CloseConnection(true, "pairing cancelled") } - - return nil } // Process reported mDNS services @@ -546,15 +672,16 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]MdnsEntry) { continue } - // Check if the remote service is paired - remoteService, err := h.PairedServiceForSKI(ski) - if err != nil { + // Check if the remote service is paired or queued for connection + service := h.serviceForSKI(ski) + pairingState := service.PairingDetail.State + if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != PairingStateQueued { continue } // patch the addresses list if an IPv4 address was provided - if remoteService.IPv4() != "" { - if ip := net.ParseIP(remoteService.IPv4()); ip != nil { + if service.IPv4 != "" { + if ip := net.ParseIP(service.IPv4); ip != nil { entry.Addresses = []net.IP{ip} } } @@ -569,6 +696,9 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]MdnsEntry) { b := strings.ToLower(item2.Brand + item2.Model + item2.Ski) return a < b }) + + h.knownMdnsEntries = mdnsEntries + h.serviceProvider.VisibleMDNSRecordsUpdated(mdnsEntries) } @@ -591,10 +721,7 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry MdnsEn h.setConnectionAttemptRunning(ski, false) // check if the remoteService still exists - remoteService, err := h.PairedServiceForSKI(ski) - if err != nil { - return - } + service := h.serviceForSKI(ski) // check if the current counter is still the same, otherwise this counter is irrelevant currentCounter, exists := h.getCurrentConnectionAttemptCounter(ski) @@ -603,7 +730,9 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry MdnsEn } // connection attempt is not relevant if the device is no longer paired - if !h.IsRemoteServiceForSKIPaired(ski) { + // or it is not queued for pairing + pairingState := h.serviceForSKI(ski).PairingDetail.State + if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != PairingStateQueued { return } @@ -613,7 +742,7 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry MdnsEn } // now initiate the connection - if success := h.initateConnection(remoteService, entry); !success { + if success := h.initateConnection(service, entry); !success { h.checkRestartMdnsSearch() } @@ -627,9 +756,16 @@ func (h *connectionsHub) initateConnection(remoteService *ServiceDetails, entry // try connecting via an IP address first for _, address := range entry.Addresses { - logging.Log.Debug("trying to connect to", remoteService.SKI(), "at", address) + // connection attempt is not relevant if the device is no longer paired + // or it is not queued for pairing + pairingState := h.serviceForSKI(remoteService.SKI).PairingDetail.State + if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != PairingStateQueued { + return false + } + + logging.Log.Debug("trying to connect to", remoteService.SKI, "at", address) if err = h.connectFoundService(remoteService, address.String(), strconv.Itoa(entry.Port)); err != nil { - logging.Log.Debug("connection to", remoteService.SKI(), "failed: ", err) + logging.Log.Debug("connection to", remoteService.SKI, "failed: ", err) } else { return true } @@ -637,9 +773,9 @@ func (h *connectionsHub) initateConnection(remoteService *ServiceDetails, entry // connectdion via IP address failed, try hostname if len(entry.Host) > 0 { - logging.Log.Debug("trying to connect to", remoteService.SKI(), "at", entry.Host) + logging.Log.Debug("trying to connect to", remoteService.SKI, "at", entry.Host) if err = h.connectFoundService(remoteService, entry.Host, strconv.Itoa(entry.Port)); err != nil { - logging.Log.Debugf("connection to %s failed: %s", remoteService.SKI(), err) + logging.Log.Debugf("connection to %s failed: %s", remoteService.SKI, err) } else { return true } @@ -704,7 +840,7 @@ func (h *connectionsHub) getConnectionInitiationDelayTime(ski string) (int, time // seed with the local SKI for initializing rand // TODO: remove when upping minimum go version to 1.20 i := new(big.Int) - hex := fmt.Sprintf("0x%s", h.localService.SKI()) + hex := fmt.Sprintf("0x%s", h.localService.SKI) randSource := rand.NewSource(time.Now().UnixNano()) if _, err := fmt.Sscan(hex, i); err == nil { randSource = rand.NewSource(i.Int64() + time.Now().UnixNano()) diff --git a/service/hub_test.go b/service/hub_test.go index 7958850d..75f047ed 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/enbility/eebus-go/ship" + gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -21,6 +22,9 @@ type testStruct struct { type HubSuite struct { suite.Suite + serviceProvider *MockServiceProvider + mdnsService *MockMdnsService + tests []testStruct } @@ -38,24 +42,20 @@ func (s *HubSuite) SetupSuite() { {9, connectionInitiationDelayTimeRanges[2]}, {10, connectionInitiationDelayTimeRanges[2]}, } -} - -// Service Provider Interface -var _ serviceProvider = (*HubSuite)(nil) -func (s *HubSuite) VisibleMDNSRecordsUpdated([]MdnsEntry) {} -func (s *HubSuite) RemoteSKIConnected(string) {} -func (s *HubSuite) RemoteSKIDisconnected(string) {} -func (s *HubSuite) ReportServiceShipID(string, string) {} + ctrl := gomock.NewController(s.T()) -var _ MdnsService = (*HubSuite)(nil) + s.serviceProvider = NewMockServiceProvider(ctrl) + s.serviceProvider.EXPECT().RemoteSKIConnected(gomock.Any()).AnyTimes() + s.serviceProvider.EXPECT().ServiceShipIDUpdate(gomock.Any(), gomock.Any()).AnyTimes() + s.serviceProvider.EXPECT().ServicePairingDetailUpdate(gomock.Any(), gomock.Any()).AnyTimes() -func (s *HubSuite) SetupMdnsService() error { return nil } -func (s *HubSuite) ShutdownMdnsService() {} -func (s *HubSuite) AnnounceMdnsEntry() error { return nil } -func (s *HubSuite) UnannounceMdnsEntry() {} -func (s *HubSuite) RegisterMdnsSearch(cb MdnsSearch) {} -func (s *HubSuite) UnregisterMdnsSearch(cb MdnsSearch) {} + s.mdnsService = NewMockMdnsService(ctrl) + s.mdnsService.EXPECT().SetupMdnsService().AnyTimes() + s.mdnsService.EXPECT().AnnounceMdnsEntry().AnyTimes() + s.mdnsService.EXPECT().UnannounceMdnsEntry().AnyTimes() + s.mdnsService.EXPECT().UnregisterMdnsSearch(gomock.Any()).AnyTimes() +} func (s *HubSuite) Test_NewConnectionsHub() { ski := "12af9e" @@ -63,7 +63,8 @@ func (s *HubSuite) Test_NewConnectionsHub() { configuration := &Configuration{ interfaces: []string{"en0"}, } - hub := newConnectionsHub(s, s, nil, configuration, localService) + + hub := newConnectionsHub(s.serviceProvider, s.mdnsService, nil, configuration, localService) assert.NotNil(s.T(), hub) hub.start() @@ -73,24 +74,24 @@ func (s *HubSuite) Test_IsRemoteSKIPaired() { sut := connectionsHub{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), + remoteServices: make(map[string]*ServiceDetails), + serviceProvider: s.serviceProvider, } ski := "test" paired := sut.IsRemoteServiceForSKIPaired(ski) assert.Equal(s.T(), false, paired) - service := NewServiceDetails(ski) // mark it as connected, so mDNS is not triggered sut.connections[ski] = &ship.ShipConnection{} - sut.PairRemoteService(service) + sut.EnablePairingForSKI(ski, true) paired = sut.IsRemoteServiceForSKIPaired(ski) assert.Equal(s.T(), true, paired) // remove the connection, so the test doesn't try to close it delete(sut.connections, ski) - err := sut.UnpairRemoteService(ski) - assert.Nil(s.T(), err) + sut.EnablePairingForSKI(ski, false) paired = sut.IsRemoteServiceForSKIPaired(ski) assert.Equal(s.T(), false, paired) } @@ -106,7 +107,7 @@ func (s *HubSuite) Test_CheckRestartMdnsSearch() { func (s *HubSuite) Test_ReportServiceShipID() { sut := connectionsHub{ - serviceProvider: s, + serviceProvider: s.serviceProvider, } sut.ReportServiceShipID("", "") // Nothing to verify yet @@ -126,7 +127,7 @@ func (s *HubSuite) Test_RegisterConnection() { sut := connectionsHub{ connections: make(map[string]*ship.ShipConnection), - mdns: s, + mdns: s.mdnsService, localService: localService, } diff --git a/service/mdns.go b/service/mdns.go index 72c016ff..c1a068c1 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -31,6 +31,8 @@ type MdnsEntry struct { Addresses []net.IP // mandatory } +//go:generate mockgen -destination=mock_mdns.go -package=service github.com/enbility/eebus-go/service MdnsSearch,MdnsService + // implemented by hubConnection, used by mdns type MdnsSearch interface { ReportMdnsEntries(entries map[string]MdnsEntry) @@ -282,7 +284,7 @@ func (m *mdns) RegisterMdnsSearch(cb MdnsSearch) { return } - // may this is already found + // maybe entries are already found mdnsEntries := m.entries go m.searchDelegate.ReportMdnsEntries(mdnsEntries) @@ -497,6 +499,8 @@ func (m *mdns) processMdnsEntry(elements map[string]string, name, host string, a m.mux.Lock() defer m.mux.Unlock() + updated := true + _, exists := m.entries[ski] if remove && exists { @@ -505,6 +509,8 @@ func (m *mdns) processMdnsEntry(elements map[string]string, name, host string, a delete(m.entries, ski) } else if exists { // update + updated = false + // avahi sends an item for each network address, merge them entry := m.entries[ski] @@ -522,6 +528,7 @@ func (m *mdns) processMdnsEntry(elements map[string]string, name, host string, a if isNewElement { entry.Addresses = append(entry.Addresses, address) + updated = true } } @@ -548,7 +555,7 @@ func (m *mdns) processMdnsEntry(elements map[string]string, name, host string, a return } - if m.searchDelegate != nil { + if m.searchDelegate != nil && updated { mdnsEntries := m.entries go m.searchDelegate.ReportMdnsEntries(mdnsEntries) } diff --git a/service/mock_hub.go b/service/mock_hub.go new file mode 100644 index 00000000..a26ea7d1 --- /dev/null +++ b/service/mock_hub.go @@ -0,0 +1,108 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/enbility/eebus-go/service (interfaces: ServiceProvider) + +// Package service is a generated GoMock package. +package service + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockServiceProvider is a mock of ServiceProvider interface. +type MockServiceProvider struct { + ctrl *gomock.Controller + recorder *MockServiceProviderMockRecorder +} + +// MockServiceProviderMockRecorder is the mock recorder for MockServiceProvider. +type MockServiceProviderMockRecorder struct { + mock *MockServiceProvider +} + +// NewMockServiceProvider creates a new mock instance. +func NewMockServiceProvider(ctrl *gomock.Controller) *MockServiceProvider { + mock := &MockServiceProvider{ctrl: ctrl} + mock.recorder = &MockServiceProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockServiceProvider) EXPECT() *MockServiceProviderMockRecorder { + return m.recorder +} + +// AllowWaitingForTrust mocks base method. +func (m *MockServiceProvider) AllowWaitingForTrust(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AllowWaitingForTrust", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. +func (mr *MockServiceProviderMockRecorder) AllowWaitingForTrust(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockServiceProvider)(nil).AllowWaitingForTrust), arg0) +} + +// RemoteSKIConnected mocks base method. +func (m *MockServiceProvider) RemoteSKIConnected(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoteSKIConnected", arg0) +} + +// RemoteSKIConnected indicates an expected call of RemoteSKIConnected. +func (mr *MockServiceProviderMockRecorder) RemoteSKIConnected(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIConnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIConnected), arg0) +} + +// RemoteSKIDisconnected mocks base method. +func (m *MockServiceProvider) RemoteSKIDisconnected(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoteSKIDisconnected", arg0) +} + +// RemoteSKIDisconnected indicates an expected call of RemoteSKIDisconnected. +func (mr *MockServiceProviderMockRecorder) RemoteSKIDisconnected(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIDisconnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIDisconnected), arg0) +} + +// ServicePairingDetailUpdate mocks base method. +func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 PairingDetail) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ServicePairingDetailUpdate", arg0, arg1) +} + +// ServicePairingDetailUpdate indicates an expected call of ServicePairingDetailUpdate. +func (mr *MockServiceProviderMockRecorder) ServicePairingDetailUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServicePairingDetailUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServicePairingDetailUpdate), arg0, arg1) +} + +// ServiceShipIDUpdate mocks base method. +func (m *MockServiceProvider) ServiceShipIDUpdate(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ServiceShipIDUpdate", arg0, arg1) +} + +// ServiceShipIDUpdate indicates an expected call of ServiceShipIDUpdate. +func (mr *MockServiceProviderMockRecorder) ServiceShipIDUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceShipIDUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServiceShipIDUpdate), arg0, arg1) +} + +// VisibleMDNSRecordsUpdated mocks base method. +func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []MdnsEntry) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "VisibleMDNSRecordsUpdated", arg0) +} + +// VisibleMDNSRecordsUpdated indicates an expected call of VisibleMDNSRecordsUpdated. +func (mr *MockServiceProviderMockRecorder) VisibleMDNSRecordsUpdated(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleMDNSRecordsUpdated", reflect.TypeOf((*MockServiceProvider)(nil).VisibleMDNSRecordsUpdated), arg0) +} diff --git a/service/mock_mdns.go b/service/mock_mdns.go new file mode 100644 index 00000000..45d6efc3 --- /dev/null +++ b/service/mock_mdns.go @@ -0,0 +1,145 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/enbility/eebus-go/service (interfaces: MdnsSearch,MdnsService) + +// Package service is a generated GoMock package. +package service + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockMdnsSearch is a mock of MdnsSearch interface. +type MockMdnsSearch struct { + ctrl *gomock.Controller + recorder *MockMdnsSearchMockRecorder +} + +// MockMdnsSearchMockRecorder is the mock recorder for MockMdnsSearch. +type MockMdnsSearchMockRecorder struct { + mock *MockMdnsSearch +} + +// NewMockMdnsSearch creates a new mock instance. +func NewMockMdnsSearch(ctrl *gomock.Controller) *MockMdnsSearch { + mock := &MockMdnsSearch{ctrl: ctrl} + mock.recorder = &MockMdnsSearchMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMdnsSearch) EXPECT() *MockMdnsSearchMockRecorder { + return m.recorder +} + +// ReportMdnsEntries mocks base method. +func (m *MockMdnsSearch) ReportMdnsEntries(arg0 map[string]MdnsEntry) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReportMdnsEntries", arg0) +} + +// ReportMdnsEntries indicates an expected call of ReportMdnsEntries. +func (mr *MockMdnsSearchMockRecorder) ReportMdnsEntries(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportMdnsEntries", reflect.TypeOf((*MockMdnsSearch)(nil).ReportMdnsEntries), arg0) +} + +// MockMdnsService is a mock of MdnsService interface. +type MockMdnsService struct { + ctrl *gomock.Controller + recorder *MockMdnsServiceMockRecorder +} + +// MockMdnsServiceMockRecorder is the mock recorder for MockMdnsService. +type MockMdnsServiceMockRecorder struct { + mock *MockMdnsService +} + +// NewMockMdnsService creates a new mock instance. +func NewMockMdnsService(ctrl *gomock.Controller) *MockMdnsService { + mock := &MockMdnsService{ctrl: ctrl} + mock.recorder = &MockMdnsServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMdnsService) EXPECT() *MockMdnsServiceMockRecorder { + return m.recorder +} + +// AnnounceMdnsEntry mocks base method. +func (m *MockMdnsService) AnnounceMdnsEntry() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AnnounceMdnsEntry") + ret0, _ := ret[0].(error) + return ret0 +} + +// AnnounceMdnsEntry indicates an expected call of AnnounceMdnsEntry. +func (mr *MockMdnsServiceMockRecorder) AnnounceMdnsEntry() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnnounceMdnsEntry", reflect.TypeOf((*MockMdnsService)(nil).AnnounceMdnsEntry)) +} + +// RegisterMdnsSearch mocks base method. +func (m *MockMdnsService) RegisterMdnsSearch(arg0 MdnsSearch) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterMdnsSearch", arg0) +} + +// RegisterMdnsSearch indicates an expected call of RegisterMdnsSearch. +func (mr *MockMdnsServiceMockRecorder) RegisterMdnsSearch(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).RegisterMdnsSearch), arg0) +} + +// SetupMdnsService mocks base method. +func (m *MockMdnsService) SetupMdnsService() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetupMdnsService") + ret0, _ := ret[0].(error) + return ret0 +} + +// SetupMdnsService indicates an expected call of SetupMdnsService. +func (mr *MockMdnsServiceMockRecorder) SetupMdnsService() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupMdnsService", reflect.TypeOf((*MockMdnsService)(nil).SetupMdnsService)) +} + +// ShutdownMdnsService mocks base method. +func (m *MockMdnsService) ShutdownMdnsService() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ShutdownMdnsService") +} + +// ShutdownMdnsService indicates an expected call of ShutdownMdnsService. +func (mr *MockMdnsServiceMockRecorder) ShutdownMdnsService() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShutdownMdnsService", reflect.TypeOf((*MockMdnsService)(nil).ShutdownMdnsService)) +} + +// UnannounceMdnsEntry mocks base method. +func (m *MockMdnsService) UnannounceMdnsEntry() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UnannounceMdnsEntry") +} + +// UnannounceMdnsEntry indicates an expected call of UnannounceMdnsEntry. +func (mr *MockMdnsServiceMockRecorder) UnannounceMdnsEntry() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnannounceMdnsEntry", reflect.TypeOf((*MockMdnsService)(nil).UnannounceMdnsEntry)) +} + +// UnregisterMdnsSearch mocks base method. +func (m *MockMdnsService) UnregisterMdnsSearch(arg0 MdnsSearch) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UnregisterMdnsSearch", arg0) +} + +// UnregisterMdnsSearch indicates an expected call of UnregisterMdnsSearch. +func (mr *MockMdnsServiceMockRecorder) UnregisterMdnsSearch(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).UnregisterMdnsSearch), arg0) +} diff --git a/service/mocks/MdnsSearch.go b/service/mocks/MdnsSearch.go new file mode 100644 index 00000000..49a6785d --- /dev/null +++ b/service/mocks/MdnsSearch.go @@ -0,0 +1,33 @@ +// Code generated by mockery v2.26.1. DO NOT EDIT. + +package mocks + +import ( + service "github.com/enbility/eebus-go/service" + mock "github.com/stretchr/testify/mock" +) + +// MdnsSearch is an autogenerated mock type for the MdnsSearch type +type MdnsSearch struct { + mock.Mock +} + +// ReportMdnsEntries provides a mock function with given fields: entries +func (_m *MdnsSearch) ReportMdnsEntries(entries map[string]service.MdnsEntry) { + _m.Called(entries) +} + +type mockConstructorTestingTNewMdnsSearch interface { + mock.TestingT + Cleanup(func()) +} + +// NewMdnsSearch creates a new instance of MdnsSearch. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMdnsSearch(t mockConstructorTestingTNewMdnsSearch) *MdnsSearch { + mock := &MdnsSearch{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/service/mocks/MdnsService.go b/service/mocks/MdnsService.go new file mode 100644 index 00000000..780ac721 --- /dev/null +++ b/service/mocks/MdnsService.go @@ -0,0 +1,76 @@ +// Code generated by mockery v2.26.1. DO NOT EDIT. + +package mocks + +import ( + service "github.com/enbility/eebus-go/service" + mock "github.com/stretchr/testify/mock" +) + +// MdnsService is an autogenerated mock type for the MdnsService type +type MdnsService struct { + mock.Mock +} + +// AnnounceMdnsEntry provides a mock function with given fields: +func (_m *MdnsService) AnnounceMdnsEntry() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RegisterMdnsSearch provides a mock function with given fields: cb +func (_m *MdnsService) RegisterMdnsSearch(cb service.MdnsSearch) { + _m.Called(cb) +} + +// SetupMdnsService provides a mock function with given fields: +func (_m *MdnsService) SetupMdnsService() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ShutdownMdnsService provides a mock function with given fields: +func (_m *MdnsService) ShutdownMdnsService() { + _m.Called() +} + +// UnannounceMdnsEntry provides a mock function with given fields: +func (_m *MdnsService) UnannounceMdnsEntry() { + _m.Called() +} + +// UnregisterMdnsSearch provides a mock function with given fields: cb +func (_m *MdnsService) UnregisterMdnsSearch(cb service.MdnsSearch) { + _m.Called(cb) +} + +type mockConstructorTestingTNewMdnsService interface { + mock.TestingT + Cleanup(func()) +} + +// NewMdnsService creates a new instance of MdnsService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMdnsService(t mockConstructorTestingTNewMdnsService) *MdnsService { + mock := &MdnsService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/service/service.go b/service/service.go index 3fafabc8..d226d09a 100644 --- a/service/service.go +++ b/service/service.go @@ -10,12 +10,19 @@ import ( "github.com/enbility/eebus-go/spine/model" ) +type RemoteService struct { + Name string `json:"name"` + Ski string `json:"ski"` + Identifier string `json:"identifier"` + Brand string `json:"brand"` + Type string `json:"type"` + Model string `json:"model"` +} + // interface for receiving data for specific events type EEBUSServiceHandler interface { - // RemoteServicesListUpdated(services []ServiceDetails) - // report all currently visible EEBUS services - VisibleMDNSRecordsUpdated(service *EEBUSService, entries []MdnsEntry) + VisibleRemoteServicesUpdated(service *EEBUSService, entries []RemoteService) // report a connection to a SKI RemoteSKIConnected(service *EEBUSService, ski string) @@ -26,7 +33,15 @@ type EEBUSServiceHandler interface { // Provides the SHIP ID the remote service reported during the handshake process // This needs to be persisted and passed on for future remote service connections // when using `PairRemoteService` - ReportServiceShipID(ski string, shipdID string) + ServiceShipIDUpdate(ski string, shipdID string) + + // Provides the current pairing state for the remote service + // This is called whenever the state changes and can be used to + // provide user information for the pairing/connection process + ServicePairingDetailUpdate(ski string, detail PairingDetail) + + // return if the user is still able to trust the connection + AllowWaitingForTrust(ski string) bool } // A service is the central element of an EEBUS service @@ -56,10 +71,24 @@ func NewEEBUSService(configuration *Configuration, serviceHandler EEBUSServiceHa } } -var _ serviceProvider = (*EEBUSService)(nil) +var _ ServiceProvider = (*EEBUSService)(nil) func (s *EEBUSService) VisibleMDNSRecordsUpdated(entries []MdnsEntry) { - s.serviceHandler.VisibleMDNSRecordsUpdated(s, entries) + var remoteServices []RemoteService + + for _, entry := range entries { + remoteService := RemoteService{ + Name: entry.Name, + Ski: entry.Ski, + Identifier: entry.Identifier, + Brand: entry.Brand, + Type: entry.Type, + Model: entry.Model, + } + + remoteServices = append(remoteServices, remoteService) + } + s.serviceHandler.VisibleRemoteServicesUpdated(s, remoteServices) } // report a connection to a SKI @@ -73,8 +102,25 @@ func (s *EEBUSService) RemoteSKIDisconnected(ski string) { } // Provides the SHIP ID the remote service reported during the handshake process -func (s *EEBUSService) ReportServiceShipID(ski string, shipdID string) { - s.serviceHandler.ReportServiceShipID(ski, shipdID) +func (s *EEBUSService) ServiceShipIDUpdate(ski string, shipdID string) { + s.serviceHandler.ServiceShipIDUpdate(ski, shipdID) +} + +// Provides the current pairing state for the remote service +// This is called whenever the state changes and can be used to +// provide user information for the pairing/connection process +func (s *EEBUSService) ServicePairingDetailUpdate(ski string, detail PairingDetail) { + s.serviceHandler.ServicePairingDetailUpdate(ski, detail) +} + +// Get the current pairing details for a given SKI +func (s *EEBUSService) PairingDetailForSki(ski string) PairingDetail { + return s.connectionsHub.PairingDetailForSki(ski) +} + +// return if the user is still able to trust the connection +func (s *EEBUSService) AllowWaitingForTrust(ski string) bool { + return s.serviceHandler.AllowWaitingForTrust(ski) } // Starts browsing for any EEBUS mDNS entry @@ -123,9 +169,9 @@ func (s *EEBUSService) Setup() error { // I assume those two to mean the same. // TODO: clarify s.LocalService = NewServiceDetails(ski) - s.LocalService.SetShipID(sd.Identifier()) - s.LocalService.SetDeviceType(sd.deviceType) - s.LocalService.SetRegisterAutoAccept(sd.registerAutoAccept) + s.LocalService.ShipID = sd.Identifier() + s.LocalService.DeviceType = sd.deviceType + s.LocalService.RegisterAutoAccept = sd.registerAutoAccept logging.Log.Info("Local SKI: ", ski) @@ -168,7 +214,7 @@ func (s *EEBUSService) Setup() error { s.spineLocalDevice.AddEntity(entity) // setup mDNS - mdns := newMDNS(s.LocalService.SKI(), s.Configuration) + mdns := newMDNS(s.LocalService.SKI, s.Configuration) // Setup connections hub with mDNS and websocket connection handling s.connectionsHub = newConnectionsHub(s, mdns, s.spineLocalDevice, s.Configuration, s.LocalService) @@ -229,21 +275,25 @@ func (s *EEBUSService) RemoteDeviceOfType(deviceType model.DeviceTypeType) *spin return nil } +// Returns the Service detail of a given remote SKI +func (s *EEBUSService) RemoteServiceForSKI(ski string) *ServiceDetails { + return s.connectionsHub.serviceForSKI(ski) +} + // Adds a new device to the list of known devices which can be connected to // and connect it if it is currently not connected -func (s *EEBUSService) PairRemoteService(service *ServiceDetails) { - s.connectionsHub.PairRemoteService(service) +func (s *EEBUSService) EnablePairingForSKI(ski string, enable bool) { + s.connectionsHub.EnablePairingForSKI(ski, enable) } -// Returns if the provided SKI is from a registered service -func (s *EEBUSService) IsRemoteServiceForSKIPaired(ski string) bool { - return s.connectionsHub.IsRemoteServiceForSKIPaired(ski) +// Triggers the pairing process for a SKI +func (s *EEBUSService) InitiatePairingWithSKI(ski string) { + s.connectionsHub.InitiatePairingWithSKI(ski) } -// Remove a device from the list of known devices which can be connected to -// and disconnect it if it is currently connected -func (s *EEBUSService) UnpairRemoteService(ski string) error { - return s.connectionsHub.UnpairRemoteService(ski) +// Cancels the pairing process for a SKI +func (s *EEBUSService) CancelPairingWithSKI(ski string) { + s.connectionsHub.CancelPairingWithSKI(ski) } // Close a connection to a remote SKI diff --git a/service/types.go b/service/types.go index 4434fe9e..249ccc32 100644 --- a/service/types.go +++ b/service/types.go @@ -2,6 +2,7 @@ package service import ( "crypto/tls" + "errors" "fmt" "github.com/enbility/eebus-go/spine/model" @@ -10,78 +11,66 @@ import ( const defaultPort int = 4711 +// pairing state for global usage, e.g. UI +type PairingState uint + +const ( + PairingStateNone PairingState = iota // The initial state, when no pairing exists + PairingStateQueued // The pairing request has been started and is pending connection initialization + PairingStateInitiated // This service initiated the pairing process + PairingStateReceived // A remote service initiated the pairing process + PairingStateInProgress // The pairing handshake is in progress + PairingStatePin // PIN processing, not supported right now! + PairingStateAccepted // The pairing handshake is accepted from both ends + PairingStateError // The pairing resulted in an error +) + +// the pairing state of a service and error if applicable +type PairingDetail struct { + State PairingState + Error error +} + // generic service details about the local or any remote service type ServiceDetails struct { // This is the SKI of the service // This needs to be persisted - ski string + SKI string // This is the IPv4 address of the device running the service // This is optional only needed when this runs with // zeroconf as mDNS and the remote device is using the latest // avahi version and thus zeroconf can sometimes not detect // the IPv4 address and not initiate a connection - ipv4 string + IPv4 string // shipID is the SHIP identifier of the service // This needs to be persisted - shipID string + ShipID string // The EEBUS device type of the device model - deviceType model.DeviceTypeType + DeviceType model.DeviceTypeType // Flags if the service auto auto accepts other services - registerAutoAccept bool + RegisterAutoAccept bool + + // Flags if the service is paired and should be connected to + // Should be enabled after the pairing process resulted PairingDetail = PairingStateAccepted + Paired bool + + // the current pairing details + PairingDetail PairingDetail } // create a new ServiceDetails record with a SKI func NewServiceDetails(ski string) *ServiceDetails { service := &ServiceDetails{ - ski: util.NormalizeSKI(ski), // standardize the provided SKI strings + SKI: util.NormalizeSKI(ski), // standardize the provided SKI strings } return service } -// return the services SKI -func (s *ServiceDetails) SKI() string { - return s.ski -} - -// SHIP ID is the ship identifier of the service -func (s *ServiceDetails) SetShipID(shipId string) { - s.shipID = shipId -} - -// Return the services SHIP ID -func (s *ServiceDetails) ShipID() string { - return s.shipID -} - -func (s *ServiceDetails) SetIPv4(ipv4 string) { - s.ipv4 = ipv4 -} - -func (s *ServiceDetails) IPv4() string { - return s.ipv4 -} - -func (s *ServiceDetails) SetDeviceType(deviceType model.DeviceTypeType) { - s.deviceType = deviceType -} - -func (s *ServiceDetails) DeviceType() model.DeviceTypeType { - return s.deviceType -} - -func (s *ServiceDetails) SetRegisterAutoAccept(auto bool) { - s.registerAutoAccept = auto -} - -func (s *ServiceDetails) RegisterAutoAccept() bool { - return s.registerAutoAccept -} - // defines requires meta information about this service type Configuration struct { // The vendors IANA PEN, optional but highly recommended. @@ -144,6 +133,11 @@ type Configuration struct { // This is useful when e.g. power values are not available and therefor // need to be calculated using the current values voltage float64 + + // Automatically retry a connection that ends in a SHIP abort, error or timeout + // Defaut: false + // Useful if the service does not have a pairing UI + autoRetryShipHandshake bool } // Setup a Configuration with the required parameters @@ -197,6 +191,13 @@ func NewConfiguration( return configuration, nil } +// defines if SHIP handshakes not completing successfully should +// also be considered as disconnections that need to be retried automatically +// default: false +func (s *Configuration) SetAutoRetryShipHandshake(retry bool) { + s.autoRetryShipHandshake = retry +} + // define an alternative mDNS and SHIP identifier // usually this is only used when no deviceCode is available or identical to the brand // if this is not set, generated identifier is used @@ -258,3 +259,9 @@ func (s *Configuration) MdnsServiceName() string { func (s *Configuration) Voltage() float64 { return s.voltage } + +// ErrServiceNotPaired if the given SKI is not paired yet +var ErrServiceNotPaired = errors.New("the provided SKI is not paired") + +// ErrConnectionNotFound that there was no active connection for a given SKI found +var ErrConnectionNotFound = errors.New("no connection for provided SKI found") diff --git a/ship/connection.go b/ship/connection.go index af82b42f..29a9be10 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -15,11 +15,6 @@ import ( "github.com/enbility/eebus-go/util" ) -// implemented by connectionsHub and used by shipConnection -type ConnectionHandler interface { - HandleClosedConnection(connection *ShipConnection) -} - // A ShipConnection handles the data connection and coordinates SHIP and SPINE messages i/o type ShipConnection struct { // The ship connection mode of this connection @@ -43,8 +38,14 @@ type ShipConnection struct { // the handler for sending messages on the data connection DataHandler ShipDataConnection + // defines if connections where handshakes do not complete successfully should be automatically retried as closed connections + autoRetryHandshake bool + // The current SHIP state - smeState shipMessageExchangeState + smeState ShipMessageExchangeState + + // the current error value if SHIP state is in error + smeError error // handles timeouts for the various states // @@ -68,7 +69,7 @@ type ShipConnection struct { mux sync.Mutex } -func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler ShipDataConnection, deviceLocalCon spine.DeviceLocalConnection, role shipRole, localShipID, remoteSki, remoteShipId string) *ShipConnection { +func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler ShipDataConnection, deviceLocalCon spine.DeviceLocalConnection, role shipRole, localShipID, remoteSki, remoteShipId string, retry bool) *ShipConnection { ship := &ShipConnection{ serviceDataProvider: dataProvider, deviceLocalCon: deviceLocalCon, @@ -77,7 +78,9 @@ func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler Ship RemoteSKI: remoteSki, remoteShipID: remoteShipId, DataHandler: dataHandler, - smeState: cmiStateInitStart, + smeState: CmiStateInitStart, + smeError: nil, + autoRetryHandshake: retry, } ship.handshakeTimerStopChan = make(chan struct{}) @@ -92,6 +95,32 @@ func (c *ShipConnection) Run() { c.handleShipMessage(false, nil) } +// provides the current ship state and error value if the state is in error +func (c *ShipConnection) ShipHandshakeState() (ShipMessageExchangeState, error) { + return c.getState(), c.smeError +} + +// invoked when pairing for a pending request is approved +func (c *ShipConnection) ApprovePendingHandshake() { + state := c.getState() + if state != SmeHelloStatePendingListen { + // TODO: what to do if the state is different? + + return + } + + // TODO: move this into hs_hello.go and add tests + + // HELLO_OK + c.stopHandshakeTimer() + c.setState(SmeHelloStateReadyInit, nil) + c.handleState(false, nil) + + // TODO: check if we need to do some validations before moving on to the next state + c.setState(SmeHelloStateOk, nil) + c.handleState(false, nil) +} + // report removing a connection func (c *ShipConnection) removeRemoteDeviceConnection() { c.deviceLocalCon.RemoveRemoteDeviceConnection(c.RemoteSKI) @@ -104,7 +133,7 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { c.removeRemoteDeviceConnection() - if safe && c.smeState > cmiStateInitStart { + if safe && c.getState() > CmiStateInitStart { // SHIP 13.4.7: Connection Termination Announce closeMessage := model.ConnectionClose{ ConnectionClose: model.ConnectionCloseType{ @@ -115,13 +144,13 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { _ = c.sendShipModel(model.MsgTypeEnd, closeMessage) - if c.smeState != smeError { + if c.getState() != SmeError { return } } - c.DataHandler.CloseDataConnection() - c.serviceDataProvider.HandleConnectionClosed(c, c.smeState == smeComplete) + c.DataHandler.CloseDataConnection(4495, "Timeout") + c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeComplete) }) } @@ -184,7 +213,15 @@ func (c *ShipConnection) hasSpineDatagram(message []byte) bool { // the websocket data connection was closed from remote func (c *ShipConnection) ReportConnectionError(err error) { + c.setState(SmeError, err) + c.CloseConnection(false, "") + + state := ShipState{ + State: SmeError, + Error: err, + } + c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.RemoteSKI, state) } const payloadPlaceholder = `{"place":"holder"}` @@ -233,9 +270,9 @@ func (c *ShipConnection) sendSpineData(data []byte) error { return err } - if c.DataHandler.IsDataConnectionClosed() { + if isClosed, err := c.DataHandler.IsDataConnectionClosed(); isClosed { c.CloseConnection(false, "") - return errors.New("connection is closed") + return err } // Wrap the message into a binary message with the ship header @@ -275,9 +312,9 @@ func (c *ShipConnection) processShipJsonMessage(message []byte, target any) erro // transform a SHIP model into EEBUS specific JSON func (c *ShipConnection) shipMessage(typ byte, model interface{}) ([]byte, error) { - if c.DataHandler.IsDataConnectionClosed() { + if isClosed, err := c.DataHandler.IsDataConnectionClosed(); isClosed { c.CloseConnection(false, "") - return nil, errors.New("connection is closed") + return nil, err } if model == nil { diff --git a/ship/connection_test.go b/ship/connection_test.go index f6a706b6..b1f95fce 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -7,6 +7,7 @@ import ( "github.com/enbility/eebus-go/ship/model" "github.com/enbility/eebus-go/spine" spineModel "github.com/enbility/eebus-go/spine/model" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -20,31 +21,12 @@ type ConnectionSuite struct { sut *ShipConnection - sentMessage []byte -} - -var _ ConnectionHandler = (*ConnectionSuite)(nil) - -func (s *ConnectionSuite) HandleClosedConnection(connection *ShipConnection) {} - -var _ ShipServiceDataProvider = (*ConnectionSuite)(nil) - -func (s *ConnectionSuite) IsRemoteServiceForSKIPaired(string) bool { return true } -func (s *ConnectionSuite) HandleConnectionClosed(*ShipConnection, bool) {} -func (s *ConnectionSuite) ReportServiceShipID(string, string) {} - -var _ ShipDataConnection = (*ConnectionSuite)(nil) + shipDataProvider *MockShipServiceDataProvider + shipDataConn *MockShipDataConnection -func (s *ConnectionSuite) InitDataProcessing(dataProcessing ShipDataProcessing) {} - -func (s *ConnectionSuite) WriteMessageToDataConnection(message []byte) error { - s.sentMessage = message - return nil + sentMessage []byte } -func (s *ConnectionSuite) CloseDataConnection() {} -func (w *ConnectionSuite) IsDataConnectionClosed() bool { return false } - func (s *ConnectionSuite) SetupSuite() {} func (s *ConnectionSuite) TearDownTest() {} @@ -53,7 +35,16 @@ func (s *ConnectionSuite) BeforeTest(suiteName, testName string) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart) - s.sut = NewConnectionHandler(s, s, localDevice, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID") + ctrl := gomock.NewController(s.T()) + + s.shipDataProvider = NewMockShipServiceDataProvider(ctrl) + + s.shipDataConn = NewMockShipDataConnection(ctrl) + s.shipDataConn.EXPECT().InitDataProcessing(gomock.Any()).AnyTimes() + s.shipDataConn.EXPECT().WriteMessageToDataConnection(gomock.Any()).DoAndReturn(func(message []byte) error { s.sentMessage = message; return nil }).AnyTimes() + s.shipDataConn.EXPECT().IsDataConnectionClosed().DoAndReturn(func() (bool, error) { return false, nil }).AnyTimes() + + s.sut = NewConnectionHandler(s.shipDataProvider, s.shipDataConn, localDevice, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID", false) } func (s *ConnectionSuite) TestSendShipModel() { diff --git a/ship/handshake.go b/ship/handshake.go index 1ef1fd58..cd1aff72 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -29,12 +29,12 @@ func (c *ShipConnection) handleShipMessage(timeout bool, message []byte) { <-time.After(500 * time.Millisecond) // - c.DataHandler.CloseDataConnection() - c.serviceDataProvider.HandleConnectionClosed(c, c.smeState == smeComplete) + c.DataHandler.CloseDataConnection(4495, "timeout") + c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeComplete) case model.ConnectionClosePhaseTypeConfirm: // we got a confirmation so close this connection - c.DataHandler.CloseDataConnection() - c.serviceDataProvider.HandleConnectionClosed(c, c.smeState == smeComplete) + c.DataHandler.CloseDataConnection(4496, "close") + c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeComplete) } return @@ -45,29 +45,43 @@ func (c *ShipConnection) handleShipMessage(timeout bool, message []byte) { } // set a new handshake state and handle timers if needed -func (c *ShipConnection) setState(newState shipMessageExchangeState) { +func (c *ShipConnection) setState(newState ShipMessageExchangeState, err error) { c.mux.Lock() - defer c.mux.Unlock() + + oldState := c.smeState c.smeState = newState switch newState { - case smeHelloStateReadyInit: + case SmeHelloStateReadyInit: c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) - case smeHelloStatePendingInit: + case SmeHelloStatePendingInit: c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) - case smeHelloStateOk: + case SmeHelloStateOk: c.stopHandshakeTimer() - case smeHelloStateAbort: + case SmeHelloStateAbort: c.stopHandshakeTimer() - case smeProtHStateClientListenChoice: + case SmeProtHStateClientListenChoice: c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) - case smeProtHStateClientOk: + case SmeProtHStateClientOk: c.stopHandshakeTimer() } + + c.smeError = nil + if oldState != newState { + c.smeError = err + state := ShipState{ + State: newState, + Error: err, + } + c.mux.Unlock() + c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.RemoteSKI, state) + return + } + c.mux.Unlock() } -func (c *ShipConnection) getState() shipMessageExchangeState { +func (c *ShipConnection) getState() ShipMessageExchangeState { c.mux.Lock() defer c.mux.Unlock() @@ -77,16 +91,16 @@ func (c *ShipConnection) getState() shipMessageExchangeState { // handle handshake state transitions func (c *ShipConnection) handleState(timeout bool, message []byte) { switch c.getState() { - case smeError: + case SmeError: logging.Log.Debug(c.RemoteSKI, "connection is in error state") return // cmiStateInit - case cmiStateInitStart: + case CmiStateInitStart: // triggered without a message received c.handshakeInit_cmiStateInitStart() - case cmiStateClientWait: + case CmiStateClientWait: if timeout { c.endHandshakeWithError(errors.New("ship client handshake timeout")) return @@ -94,7 +108,7 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { c.handshakeInit_cmiStateClientWait(message) - case cmiStateServerWait: + case CmiStateServerWait: if timeout { c.endHandshakeWithError(errors.New("ship server handshake timeout")) return @@ -103,74 +117,89 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { // smeHello - case smeHelloState: - // go into the 1st substate right away - c.setState(smeHelloStateReadyInit) + case SmeHelloState: + // check if the service is already trusted or the role is client, + // which means it was initiated from this service usually by triggering the + // pairing service + // go to substate ready if so, otherwise to substate pending + + if c.serviceDataProvider.IsRemoteServiceForSKIPaired(c.RemoteSKI) || c.role == ShipRoleClient { + c.setState(SmeHelloStateReadyInit, nil) + } else { + c.setState(SmeHelloStatePendingInit, nil) + } c.handleState(timeout, message) - case smeHelloStateReadyInit: + case SmeHelloStateReadyInit: c.handshakeHello_Init() - case smeHelloStateReadyListen: + case SmeHelloStateReadyListen: if timeout { - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return } c.handshakeHello_ReadyListen(message) - case smeHelloStatePendingInit: + case SmeHelloStatePendingInit: c.handshakeHello_PendingInit() - case smeHelloStatePendingListen: + case SmeHelloStatePendingListen: if timeout { - c.handshakeHello_PendingTimeout() + // The device needs to be in a state for the user to allow trusting the device + // e.g. either the web UI or by other means + if !c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { + c.handshakeHello_PendingTimeout() + return + } + + c.handshakeHello_PendingProlongationRequest() return } c.handshakeHello_PendingListen(message) - case smeHelloStateOk: + case SmeHelloStateOk: c.handshakeProtocol_Init() - case smeHelloStateAbort: + case SmeHelloStateAbort: c.handshakeHello_Abort() // smeProtocol - case smeProtHStateServerListenProposal: + case SmeProtHStateServerListenProposal: c.handshakeProtocol_smeProtHStateServerListenProposal(message) - case smeProtHStateServerListenConfirm: + case SmeProtHStateServerListenConfirm: c.handshakeProtocol_smeProtHStateServerListenConfirm(message) - case smeProtHStateClientListenChoice: + case SmeProtHStateClientListenChoice: c.stopHandshakeTimer() c.handshakeProtocol_smeProtHStateClientListenChoice(message) - case smeProtHStateClientOk: - c.setState(smePinStateCheckInit) + case SmeProtHStateClientOk: + c.setState(SmePinStateCheckInit, nil) c.handleState(false, nil) - case smeProtHStateServerOk: - c.setState(smePinStateCheckInit) + case SmeProtHStateServerOk: + c.setState(SmePinStateCheckInit, nil) c.handleState(false, nil) // smePinState - case smePinStateCheckInit: + case SmePinStateCheckInit: c.handshakePin_Init() - case smePinStateCheckListen: + case SmePinStateCheckListen: c.handshakePin_smePinStateCheckListen(message) - case smePinStateCheckOk: + case SmePinStateCheckOk: c.handshakeAccessMethods_Init() // smeAccessMethods - case smeAccessMethodsRequest: + case SmeAccessMethodsRequest: c.handshakeAccessMethods_Request(message) } } @@ -180,14 +209,14 @@ func (c *ShipConnection) approveHandshake() { // Report to SPINE local device about this remote device connection c.spineDataProcessing = c.deviceLocalCon.AddRemoteDevice(c.RemoteSKI, c) c.stopHandshakeTimer() - c.setState(smeComplete) + c.setState(SmeComplete, nil) } // end the handshake process because of an error func (c *ShipConnection) endHandshakeWithError(err error) { c.stopHandshakeTimer() - c.setState(smeError) + c.setState(SmeError, err) logging.Log.Debug(c.RemoteSKI, "SHIP handshake error:", err) diff --git a/ship/hs_access.go b/ship/hs_access.go index 0f327b8c..8be73f88 100644 --- a/ship/hs_access.go +++ b/ship/hs_access.go @@ -23,7 +23,7 @@ func (c *ShipConnection) handshakeAccessMethods_Init() { } c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) - c.setState(smeAccessMethodsRequest) + c.setState(SmeAccessMethodsRequest, nil) } func (c *ShipConnection) handshakeAccessMethods_Request(message []byte) { @@ -76,6 +76,6 @@ func (c *ShipConnection) handshakeAccessMethods_Request(message []byte) { return } - c.setState(smeApproved) + c.setState(SmeApproved, nil) c.approveHandshake() } diff --git a/ship/hs_access_test.go b/ship/hs_access_test.go index 155569f6..edc670e3 100644 --- a/ship/hs_access_test.go +++ b/ship/hs_access_test.go @@ -20,11 +20,11 @@ type AccessSuite struct { func (s *AccessSuite) Test_Init() { sut, data := initTest(ShipRoleClient) - sut.setState(smePinStateCheckOk) + sut.setState(SmePinStateCheckOk, nil) sut.handleState(false, nil) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeAccessMethodsRequest, sut.getState()) + assert.Equal(s.T(), SmeAccessMethodsRequest, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -33,7 +33,7 @@ func (s *AccessSuite) Test_Init() { func (s *AccessSuite) Test_Request() { sut, data := initTest(ShipRoleClient) - sut.setState(smeAccessMethodsRequest) + sut.setState(SmeAccessMethodsRequest, nil) accessMsg := model.AccessMethodsRequest{ AccessMethodsRequest: model.AccessMethodsRequestType{}, @@ -45,7 +45,7 @@ func (s *AccessSuite) Test_Request() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeAccessMethodsRequest, sut.getState()) + assert.Equal(s.T(), SmeAccessMethodsRequest, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -54,7 +54,7 @@ func (s *AccessSuite) Test_Request() { func (s *AccessSuite) Test_Methods_Ok() { sut, data := initTest(ShipRoleClient) - sut.setState(smeAccessMethodsRequest) + sut.setState(SmeAccessMethodsRequest, nil) accessMsg := model.AccessMethods{ AccessMethods: model.AccessMethodsType{ @@ -68,7 +68,7 @@ func (s *AccessSuite) Test_Methods_Ok() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeComplete, sut.getState()) + assert.Equal(s.T(), SmeComplete, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -77,7 +77,7 @@ func (s *AccessSuite) Test_Methods_Ok() { func (s *AccessSuite) Test_Methods_WrongShipID() { sut, data := initTest(ShipRoleClient) - sut.setState(smeAccessMethodsRequest) + sut.setState(SmeAccessMethodsRequest, nil) accessMsg := model.AccessMethods{ AccessMethods: model.AccessMethodsType{ @@ -91,7 +91,7 @@ func (s *AccessSuite) Test_Methods_WrongShipID() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_hello.go b/ship/hs_hello.go index 2f94895e..aa15475a 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -13,19 +13,19 @@ import ( // SME_HELLO_STATE_READY_INIT func (c *ShipConnection) handshakeHello_Init() { if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypeReady, tHelloInit, false); err != nil { - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return } - c.setState(smeHelloStateReadyListen) + c.setState(SmeHelloStateReadyListen, nil) } // SME_HELLO_STATE_READY_LISTEN func (c *ShipConnection) handshakeHello_ReadyListen(message []byte) { var helloReturnMsg model.ConnectionHello if err := c.processShipJsonMessage(message, &helloReturnMsg); err != nil { - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return } @@ -35,7 +35,7 @@ func (c *ShipConnection) handshakeHello_ReadyListen(message []byte) { switch hello.Phase { case model.ConnectionHelloPhaseTypeReady: // HELLO_OK - c.setState(smeHelloStateOk) + c.setState(SmeHelloStateOk, nil) case model.ConnectionHelloPhaseTypePending: // the phase is still pending an no prolongationRequest is set, ignore the message @@ -45,21 +45,29 @@ func (c *ShipConnection) handshakeHello_ReadyListen(message []byte) { // if we got a prolongation request, accept it if *hello.ProlongationRequest { - if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, tHelloInit, false); err != nil { + if c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { + // re-init timer + c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) + } + + if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypeReady, tHelloInit, false); err != nil { c.endHandshakeWithError(err) - return } + + return } + // TODO: what to do if this is false? + case model.ConnectionHelloPhaseTypeAborted: - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return default: // don't accept any other responses logging.Log.Errorf("Unexpected connection hello phase: %s", hello.Phase) - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return } @@ -80,21 +88,20 @@ func (c *ShipConnection) handshakeHello_Abort() { } // SME_HELLO_PENDING_INIT -// TODO: clarify in which scenario and how we need to support getting into this state func (c *ShipConnection) handshakeHello_PendingInit() { if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, tHelloInit, false); err != nil { c.endHandshakeWithError(err) return } - c.setState(smeHelloStatePendingListen) + c.setState(SmeHelloStatePendingListen, nil) } // SME_HELLO_PENDING_LISTEN func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { var helloReturnMsg model.ConnectionHello if err := c.processShipJsonMessage(message, &helloReturnMsg); err != nil { - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return } @@ -104,7 +111,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { switch hello.Phase { case model.ConnectionHelloPhaseTypeReady: if hello.Waiting == nil { - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return } @@ -127,7 +134,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { if newDuration < tHelloProlongMin { // I interpret 13.4.4.1.3 Page 64 Line 1550-1553 as this resulting in a timeout state // TODO: verify this - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) } @@ -152,7 +159,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { if newDuration < tHelloProlongMin { // I interpret 13.4.4.1.3 Page 64 Line 1557-1560 as this resulting in a timeout state // TODO: verify this - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) } @@ -168,18 +175,18 @@ func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { return } - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) case model.ConnectionHelloPhaseTypeAborted: - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return default: // don't accept any other responses logging.Log.Errorf("Unexpected connection hello phase: %s", hello.Phase) - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return } @@ -187,9 +194,19 @@ func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { c.handleState(false, nil) } +func (c *ShipConnection) handshakeHello_PendingProlongationRequest() { + if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, 0, true); err != nil { + c.endHandshakeWithError(err) + return + } + + // TODO: we need to set the timer to the last received waiting value + c.setHandshakeTimer(timeoutTimerTypeProlongRequestReply, tHelloInit) +} + func (c *ShipConnection) handshakeHello_PendingTimeout() { if c.handshakeTimerType != timeoutTimerTypeSendProlongationRequest { - c.setState(smeHelloStateAbort) + c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return } diff --git a/ship/hs_hello_client_test.go b/ship/hs_hello_client_test.go index 3fffa88f..a5158d07 100644 --- a/ship/hs_hello_client_test.go +++ b/ship/hs_hello_client_test.go @@ -25,11 +25,11 @@ func (s *HelloClientSuite) BeforeTest(suiteName, testName string) { func (s *HelloClientSuite) Test_InitialState() { sut, data := initTest(s.role) - sut.setState(smeHelloState) + sut.setState(SmeHelloState, nil) sut.handleState(false, nil) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStateReadyListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -38,8 +38,8 @@ func (s *HelloClientSuite) Test_InitialState() { func (s *HelloClientSuite) Test_ReadyListen_Ok() { sut, _ := initTest(s.role) - sut.setState(smeHelloStateReadyInit) // inits the timer - sut.setState(smeHelloStateReadyListen) + sut.setState(SmeHelloStateReadyInit, nil) // inits the timer + sut.setState(SmeHelloStateReadyListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -54,7 +54,7 @@ func (s *HelloClientSuite) Test_ReadyListen_Ok() { sut.handleState(false, msg) // the state goes from smeHelloStateOk directly to smeProtHStateClientInit to smeProtHStateClientListenChoice - assert.Equal(s.T(), smeProtHStateClientListenChoice, sut.getState()) + assert.Equal(s.T(), SmeProtHStateClientListenChoice, sut.getState()) shutdownTest(sut) } diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go index 276b6bb6..cdbf1b01 100644 --- a/ship/hs_hello_test.go +++ b/ship/hs_hello_test.go @@ -26,11 +26,11 @@ func (s *HelloSuite) BeforeTest(suiteName, testName string) { func (s *HelloSuite) Test_InitialState() { sut, data := initTest(s.role) - sut.setState(smeHelloState) + sut.setState(SmeHelloState, nil) sut.handleState(false, nil) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStateReadyListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -39,7 +39,7 @@ func (s *HelloSuite) Test_InitialState() { func (s *HelloSuite) Test_ReadyListen_Init() { sut, _ := initTest(s.role) - sut.setState(smeHelloStateReadyInit) + sut.setState(SmeHelloStateReadyInit, nil) assert.Equal(s.T(), true, sut.handshakeTimerRunning) shutdownTest(sut) @@ -48,8 +48,8 @@ func (s *HelloSuite) Test_ReadyListen_Init() { func (s *HelloSuite) Test_ReadyListen_Ok() { sut, _ := initTest(s.role) - sut.setState(smeHelloStateReadyInit) // inits the timer - sut.setState(smeHelloStateReadyListen) + sut.setState(SmeHelloStateReadyInit, nil) // inits the timer + sut.setState(SmeHelloStateReadyListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -64,7 +64,7 @@ func (s *HelloSuite) Test_ReadyListen_Ok() { sut.handleState(false, msg) // the state goes from smeHelloStateOk directly to smeProtHStateServerInit to smeProtHStateClientListenProposal - assert.Equal(s.T(), smeProtHStateServerListenProposal, sut.getState()) + assert.Equal(s.T(), SmeProtHStateServerListenProposal, sut.getState()) shutdownTest(sut) } @@ -74,12 +74,12 @@ func (s *HelloSuite) Test_ReadyListen_Timeout() { sut, data := initTest(s.role) - sut.setState(smeHelloStateReadyInit) // inits the timer - sut.setState(smeHelloStateReadyListen) + sut.setState(SmeHelloStateReadyInit, nil) // inits the timer + sut.setState(SmeHelloStateReadyListen, nil) time.Sleep(tHelloInit + time.Second) - assert.Equal(s.T(), smeHelloStateAbort, sut.getState()) + assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -88,8 +88,8 @@ func (s *HelloSuite) Test_ReadyListen_Timeout() { func (s *HelloSuite) Test_ReadyListen_Ignore() { sut, _ := initTest(s.role) - sut.setState(smeHelloStateReadyInit) // inits the timer - sut.setState(smeHelloStateReadyListen) + sut.setState(SmeHelloStateReadyInit, nil) // inits the timer + sut.setState(SmeHelloStateReadyListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -103,7 +103,7 @@ func (s *HelloSuite) Test_ReadyListen_Ignore() { sut.handleState(false, msg) - assert.Equal(s.T(), smeHelloStateReadyListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) shutdownTest(sut) } @@ -111,8 +111,8 @@ func (s *HelloSuite) Test_ReadyListen_Ignore() { func (s *HelloSuite) Test_ReadyListen_Abort() { sut, data := initTest(s.role) - sut.setState(smeHelloStateReadyInit) // inits the timer - sut.setState(smeHelloStateReadyListen) + sut.setState(SmeHelloStateReadyInit, nil) // inits the timer + sut.setState(SmeHelloStateReadyListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -127,7 +127,7 @@ func (s *HelloSuite) Test_ReadyListen_Abort() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStateAbort, sut.getState()) + assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -136,11 +136,11 @@ func (s *HelloSuite) Test_ReadyListen_Abort() { func (s *HelloSuite) Test_PendingInit() { sut, data := initTest(s.role) - sut.setState(smeHelloStatePendingInit) + sut.setState(SmeHelloStatePendingInit, nil) sut.handleState(false, nil) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStatePendingListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -149,8 +149,8 @@ func (s *HelloSuite) Test_PendingInit() { func (s *HelloSuite) Test_PendingListen() { sut, _ := initTest(s.role) - sut.setState(smeHelloStatePendingInit) // inits the timer - sut.setState(smeHelloStatePendingListen) + sut.setState(SmeHelloStatePendingInit, nil) // inits the timer + sut.setState(SmeHelloStatePendingListen, nil) sut.handleState(false, nil) shutdownTest(sut) @@ -161,12 +161,12 @@ func (s *HelloSuite) Test_PendingListen_Timeout() { sut, data := initTest(s.role) - sut.setState(smeHelloStatePendingInit) // inits the timer - sut.setState(smeHelloStatePendingListen) + sut.setState(SmeHelloStatePendingInit, nil) // inits the timer + sut.setState(SmeHelloStatePendingListen, nil) time.Sleep(tHelloInit + time.Second) - assert.Equal(s.T(), smeHelloStateAbort, sut.getState()) + assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -175,8 +175,8 @@ func (s *HelloSuite) Test_PendingListen_Timeout() { func (s *HelloSuite) Test_PendingListen_ReadyAbort() { sut, data := initTest(s.role) - sut.setState(smeHelloStatePendingInit) // inits the timer - sut.setState(smeHelloStatePendingListen) + sut.setState(SmeHelloStatePendingInit, nil) // inits the timer + sut.setState(SmeHelloStatePendingListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -191,7 +191,7 @@ func (s *HelloSuite) Test_PendingListen_ReadyAbort() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStateAbort, sut.getState()) + assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -200,8 +200,8 @@ func (s *HelloSuite) Test_PendingListen_ReadyAbort() { func (s *HelloSuite) Test_PendingListen_ReadyWaiting() { sut, _ := initTest(s.role) - sut.setState(smeHelloStatePendingInit) // inits the timer - sut.setState(smeHelloStatePendingListen) + sut.setState(SmeHelloStatePendingInit, nil) // inits the timer + sut.setState(SmeHelloStatePendingListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -217,7 +217,7 @@ func (s *HelloSuite) Test_PendingListen_ReadyWaiting() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStatePendingListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) shutdownTest(sut) } @@ -225,8 +225,8 @@ func (s *HelloSuite) Test_PendingListen_ReadyWaiting() { func (s *HelloSuite) Test_PendingListen_Abort() { sut, data := initTest(s.role) - sut.setState(smeHelloStatePendingInit) // inits the timer - sut.setState(smeHelloStatePendingListen) + sut.setState(SmeHelloStatePendingInit, nil) // inits the timer + sut.setState(SmeHelloStatePendingListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -241,7 +241,7 @@ func (s *HelloSuite) Test_PendingListen_Abort() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStateAbort, sut.getState()) + assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -250,8 +250,8 @@ func (s *HelloSuite) Test_PendingListen_Abort() { func (s *HelloSuite) Test_PendingListen_PendingWaiting() { sut, _ := initTest(s.role) - sut.setState(smeHelloStatePendingInit) // inits the timer - sut.setState(smeHelloStatePendingListen) + sut.setState(SmeHelloStatePendingInit, nil) // inits the timer + sut.setState(SmeHelloStatePendingListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -267,7 +267,7 @@ func (s *HelloSuite) Test_PendingListen_PendingWaiting() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStatePendingListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) shutdownTest(sut) } @@ -275,8 +275,8 @@ func (s *HelloSuite) Test_PendingListen_PendingWaiting() { func (s *HelloSuite) Test_PendingListen_PendingProlongation() { sut, data := initTest(s.role) - sut.setState(smeHelloStatePendingInit) // inits the timer - sut.setState(smeHelloStatePendingListen) + sut.setState(SmeHelloStatePendingInit, nil) // inits the timer + sut.setState(SmeHelloStatePendingListen, nil) helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ @@ -292,7 +292,7 @@ func (s *HelloSuite) Test_PendingListen_PendingProlongation() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeHelloStatePendingListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_helper_test.go b/ship/hs_helper_test.go index 0a74ac0f..e69760b2 100644 --- a/ship/hs_helper_test.go +++ b/ship/hs_helper_test.go @@ -37,12 +37,8 @@ func (s *dataHandlerTest) WriteMessageToDataConnection(message []byte) error { return nil } -func (s *dataHandlerTest) CloseDataConnection() {} -func (w *dataHandlerTest) IsDataConnectionClosed() bool { return false } - -var _ ConnectionHandler = (*dataHandlerTest)(nil) - -func (s *dataHandlerTest) HandleClosedConnection(connection *ShipConnection) {} +func (s *dataHandlerTest) CloseDataConnection(int, string) {} +func (w *dataHandlerTest) IsDataConnectionClosed() (bool, error) { return false, nil } var _ ShipServiceDataProvider = (*dataHandlerTest)(nil) @@ -50,14 +46,16 @@ func (s *dataHandlerTest) IsRemoteServiceForSKIPaired(string) bool { return true func (s *dataHandlerTest) HandleConnectionClosed(*ShipConnection, bool) { s.handleConnectionClosedInvoked = true } -func (s *dataHandlerTest) ReportServiceShipID(string, string) {} +func (s *dataHandlerTest) ReportServiceShipID(string, string) {} +func (s *dataHandlerTest) AllowWaitingForTrust(string) bool { return false } +func (s *dataHandlerTest) HandleShipHandshakeStateUpdate(string, ShipState) {} func initTest(role shipRole) (*ShipConnection, *dataHandlerTest) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart) dataHandler := &dataHandlerTest{} - conhandler := NewConnectionHandler(dataHandler, dataHandler, localDevice, role, "LocalShipID", "RemoveDevice", "RemoteShipID") + conhandler := NewConnectionHandler(dataHandler, dataHandler, localDevice, role, "LocalShipID", "RemoveDevice", "RemoteShipID", false) return conhandler, dataHandler } diff --git a/ship/hs_init.go b/ship/hs_init.go index 291f37de..5f190d0e 100644 --- a/ship/hs_init.go +++ b/ship/hs_init.go @@ -13,14 +13,14 @@ func (c *ShipConnection) handshakeInit_cmiStateInitStart() { switch c.role { case ShipRoleClient: // CMI_STATE_CLIENT_SEND - c.setState(cmiStateClientSend) + c.setState(CmiStateClientSend, nil) if err := c.DataHandler.WriteMessageToDataConnection(shipInit); err != nil { c.endHandshakeWithError(err) return } - c.setState(cmiStateClientWait) + c.setState(CmiStateClientWait, nil) case ShipRoleServer: - c.setState(cmiStateServerWait) + c.setState(CmiStateServerWait, nil) } c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) @@ -28,7 +28,7 @@ func (c *ShipConnection) handshakeInit_cmiStateInitStart() { // CMI_STATE_SERVER_WAIT func (c *ShipConnection) handshakeInit_cmiStateServerWait(message []byte) { - c.smeState = cmiStateServerEvaluate + c.setState(CmiStateServerEvaluate, nil) if !c.handshakeInit_cmiStateEvaluate(message) { return @@ -39,19 +39,19 @@ func (c *ShipConnection) handshakeInit_cmiStateServerWait(message []byte) { return } - c.setState(smeHelloState) + c.setState(SmeHelloState, nil) c.handleState(false, nil) } // CMI_STATE_CLIENT_WAIT func (c *ShipConnection) handshakeInit_cmiStateClientWait(message []byte) { - c.smeState = cmiStateClientEvaluate + c.setState(CmiStateClientEvaluate, nil) if !c.handshakeInit_cmiStateEvaluate(message) { return } - c.setState(smeHelloState) + c.setState(SmeHelloState, nil) c.handleState(false, nil) } @@ -65,7 +65,7 @@ func (c *ShipConnection) handshakeInit_cmiStateEvaluate(message []byte) bool { c.endHandshakeWithError(fmt.Errorf("Invalid SHIP MessageType, expected 0 and got %s" + string(msgType))) return false } - if data[0] != byte(0) { + if len(data) > 0 && data[0] != byte(0) { c.endHandshakeWithError(fmt.Errorf("Invalid SHIP MessageValue, expected 0 and got %s" + string(data))) return false } diff --git a/ship/hs_init_client_test.go b/ship/hs_init_client_test.go index d12861d0..01b89439 100644 --- a/ship/hs_init_client_test.go +++ b/ship/hs_init_client_test.go @@ -24,7 +24,7 @@ func (s *InitClientSuite) BeforeTest(suiteName, testName string) { func (s *InitClientSuite) Test_Init() { sut, _ := initTest(s.role) - assert.Equal(s.T(), cmiStateInitStart, sut.getState()) + assert.Equal(s.T(), CmiStateInitStart, sut.getState()) shutdownTest(sut) } @@ -32,12 +32,12 @@ func (s *InitClientSuite) Test_Init() { func (s *InitClientSuite) Test_Start() { sut, data := initTest(s.role) - sut.setState(cmiStateInitStart) + sut.setState(CmiStateInitStart, nil) sut.handleState(false, nil) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), cmiStateClientWait, sut.getState()) + assert.Equal(s.T(), CmiStateClientWait, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) assert.Equal(s.T(), shipInit, data.lastMessage()) @@ -47,12 +47,12 @@ func (s *InitClientSuite) Test_Start() { func (s *InitClientSuite) Test_ClientWait() { sut, data := initTest(s.role) - sut.setState(cmiStateClientWait) + sut.setState(CmiStateClientWait, nil) sut.handleState(false, shipInit) // the state goes from smeHelloState directly to smeHelloStateReadyInit to smeHelloStateReadyListen - assert.Equal(s.T(), smeHelloStateReadyListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -61,11 +61,11 @@ func (s *InitClientSuite) Test_ClientWait() { func (s *InitClientSuite) Test_ClientWait_Timeout() { sut, data := initTest(s.role) - sut.setState(cmiStateClientWait) + sut.setState(CmiStateClientWait, nil) sut.handleState(true, nil) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) assert.Equal(s.T(), data.handleConnectionClosedInvoked, true) @@ -75,11 +75,11 @@ func (s *InitClientSuite) Test_ClientWait_Timeout() { func (s *InitClientSuite) Test_ClientWait_InvalidMsgType() { sut, data := initTest(s.role) - sut.setState(cmiStateClientWait) + sut.setState(CmiStateClientWait, nil) sut.handleState(false, []byte{0x05, 0x00}) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -88,11 +88,11 @@ func (s *InitClientSuite) Test_ClientWait_InvalidMsgType() { func (s *InitClientSuite) Test_ClientWait_InvalidData() { sut, data := initTest(s.role) - sut.setState(cmiStateClientWait) + sut.setState(CmiStateClientWait, nil) sut.handleState(false, []byte{model.MsgTypeInit, 0x05}) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_init_server_test.go b/ship/hs_init_server_test.go index 913be6bb..4ec129a7 100644 --- a/ship/hs_init_server_test.go +++ b/ship/hs_init_server_test.go @@ -24,7 +24,7 @@ func (s *InitServerSuite) BeforeTest(suiteName, testName string) { func (s *InitServerSuite) Test_Init() { sut, _ := initTest(s.role) - assert.Equal(s.T(), cmiStateInitStart, sut.getState()) + assert.Equal(s.T(), CmiStateInitStart, sut.getState()) shutdownTest(sut) } @@ -32,12 +32,12 @@ func (s *InitServerSuite) Test_Init() { func (s *InitServerSuite) Test_Start() { sut, _ := initTest(s.role) - sut.setState(cmiStateInitStart) + sut.setState(CmiStateInitStart, nil) sut.handleState(false, nil) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), cmiStateServerWait, sut.getState()) + assert.Equal(s.T(), CmiStateServerWait, sut.getState()) shutdownTest(sut) } @@ -45,12 +45,12 @@ func (s *InitServerSuite) Test_Start() { func (s *InitServerSuite) Test_ServerWait() { sut, data := initTest(s.role) - sut.setState(cmiStateServerWait) + sut.setState(CmiStateServerWait, nil) sut.handleState(false, shipInit) // the state goes from smeHelloState directly to smeHelloStateReadyInit to smeHelloStateReadyListen - assert.Equal(s.T(), smeHelloStateReadyListen, sut.getState()) + assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -59,11 +59,11 @@ func (s *InitServerSuite) Test_ServerWait() { func (s *InitServerSuite) Test_ServerWait_InvalidMsgType() { sut, data := initTest(s.role) - sut.setState(cmiStateServerWait) + sut.setState(CmiStateServerWait, nil) sut.handleState(false, []byte{0x05, 0x00}) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -72,11 +72,11 @@ func (s *InitServerSuite) Test_ServerWait_InvalidMsgType() { func (s *InitServerSuite) Test_ServerWait_InvalidData() { sut, data := initTest(s.role) - sut.setState(cmiStateServerWait) + sut.setState(CmiStateServerWait, nil) sut.handleState(false, []byte{model.MsgTypeInit, 0x05}) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_pin.go b/ship/hs_pin.go index adf826d0..bbded434 100644 --- a/ship/hs_pin.go +++ b/ship/hs_pin.go @@ -10,7 +10,7 @@ import ( // Handshake Pin covers the states smePin... func (c *ShipConnection) handshakePin_Init() { - c.setState(smePinStateCheckInit) + c.setState(SmePinStateCheckInit, nil) pinState := model.ConnectionPinState{ ConnectionPinState: model.ConnectionPinStateType{ @@ -23,7 +23,7 @@ func (c *ShipConnection) handshakePin_Init() { return } - c.setState(smePinStateCheckListen) + c.setState(SmePinStateCheckListen, nil) } func (c *ShipConnection) handshakePin_smePinStateCheckListen(message []byte) { @@ -37,7 +37,7 @@ func (c *ShipConnection) handshakePin_smePinStateCheckListen(message []byte) { switch connectionPinState.ConnectionPinState.PinState { case model.PinStateTypeNone: - c.setState(smePinStateCheckOk) + c.setState(SmePinStateCheckOk, nil) c.handleState(false, nil) case model.PinStateTypeRequired: c.endHandshakeWithError(errors.New("Got pin state: required (unsupported)")) diff --git a/ship/hs_pin_test.go b/ship/hs_pin_test.go index c4ad0792..4f6a1b60 100644 --- a/ship/hs_pin_test.go +++ b/ship/hs_pin_test.go @@ -19,11 +19,11 @@ type PinSuite struct { func (s *PinSuite) Test_Init() { sut, data := initTest(ShipRoleClient) - sut.setState(smePinStateCheckInit) + sut.setState(SmePinStateCheckInit, nil) sut.handleState(false, nil) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smePinStateCheckListen, sut.getState()) + assert.Equal(s.T(), SmePinStateCheckListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -32,7 +32,7 @@ func (s *PinSuite) Test_Init() { func (s *PinSuite) Test_CheckListen_None() { sut, data := initTest(ShipRoleClient) - sut.setState(smePinStateCheckListen) + sut.setState(SmePinStateCheckListen, nil) pinState := model.ConnectionPinState{ ConnectionPinState: model.ConnectionPinStateType{ @@ -46,7 +46,7 @@ func (s *PinSuite) Test_CheckListen_None() { sut.handleState(false, msg) assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeAccessMethodsRequest, sut.getState()) + assert.Equal(s.T(), SmeAccessMethodsRequest, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -55,7 +55,7 @@ func (s *PinSuite) Test_CheckListen_None() { func (s *PinSuite) Test_CheckListen_Required() { sut, data := initTest(ShipRoleClient) - sut.setState(smePinStateCheckListen) + sut.setState(SmePinStateCheckListen, nil) pinState := model.ConnectionPinState{ ConnectionPinState: model.ConnectionPinStateType{ @@ -69,7 +69,7 @@ func (s *PinSuite) Test_CheckListen_Required() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -78,7 +78,7 @@ func (s *PinSuite) Test_CheckListen_Required() { func (s *PinSuite) Test_CheckListen_Optional() { sut, data := initTest(ShipRoleClient) - sut.setState(smePinStateCheckListen) + sut.setState(SmePinStateCheckListen, nil) pinState := model.ConnectionPinState{ ConnectionPinState: model.ConnectionPinStateType{ @@ -92,7 +92,7 @@ func (s *PinSuite) Test_CheckListen_Optional() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -101,7 +101,7 @@ func (s *PinSuite) Test_CheckListen_Optional() { func (s *PinSuite) Test_CheckListen_Ok() { sut, data := initTest(ShipRoleClient) - sut.setState(smePinStateCheckListen) + sut.setState(SmePinStateCheckListen, nil) pinState := model.ConnectionPinState{ ConnectionPinState: model.ConnectionPinStateType{ @@ -115,7 +115,7 @@ func (s *PinSuite) Test_CheckListen_Ok() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeError, sut.getState()) + assert.Equal(s.T(), SmeError, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_prot.go b/ship/hs_prot.go index 96bd46fc..3775f1bd 100644 --- a/ship/hs_prot.go +++ b/ship/hs_prot.go @@ -13,11 +13,11 @@ import ( func (c *ShipConnection) handshakeProtocol_Init() { switch c.role { case ShipRoleServer: - c.setState(smeProtHStateServerInit) + c.setState(SmeProtHStateServerInit, nil) c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) - c.setState(smeProtHStateServerListenProposal) + c.setState(SmeProtHStateServerListenProposal, nil) case ShipRoleClient: - c.setState(smeProtHStateClientInit) + c.setState(SmeProtHStateClientInit, nil) c.handshakeProtocol_smeProtHStateClientInit() } } @@ -61,7 +61,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateServerListenProposal(mes c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) - c.setState(smeProtHStateServerListenConfirm) + c.setState(SmeProtHStateServerListenConfirm, nil) } func (c *ShipConnection) handshakeProtocol_smeProtHStateServerListenConfirm(message []byte) { @@ -82,12 +82,12 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateServerListenConfirm(mess c.stopHandshakeTimer() - c.setState(smeProtHStateServerOk) + c.setState(SmeProtHStateServerOk, nil) c.handleState(false, nil) } func (c *ShipConnection) handshakeProtocol_smeProtHStateClientInit() { - c.setState(smeProtHStateClientInit) + c.setState(SmeProtHStateClientInit, nil) protocolHandshake := c.protocolHandshake() protocolHandshake.MessageProtocolHandshake.HandshakeType = model.ProtocolHandshakeTypeTypeAnnounceMax @@ -97,7 +97,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateClientInit() { return } - c.setState(smeProtHStateClientListenChoice) + c.setState(SmeProtHStateClientListenChoice, nil) } func (c *ShipConnection) handshakeProtocol_smeProtHStateClientListenChoice(message []byte) { @@ -158,7 +158,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateClientListenChoice(messa return } - c.setState(smeProtHStateClientOk) + c.setState(SmeProtHStateClientOk, nil) c.handleState(false, nil) } diff --git a/ship/hs_prot_client_test.go b/ship/hs_prot_client_test.go index 2675e231..826d37c9 100644 --- a/ship/hs_prot_client_test.go +++ b/ship/hs_prot_client_test.go @@ -25,12 +25,12 @@ func (s *ProClientSuite) BeforeTest(suiteName, testName string) { func (s *ProClientSuite) Test_Init() { sut, data := initTest(s.role) - sut.setState(smeHelloStateOk) + sut.setState(SmeHelloStateOk, nil) sut.handleState(false, nil) // the state goes from smeHelloStateOk to smeProtHStateClientInit to smeProtHStateClientListenChoice - assert.Equal(s.T(), smeProtHStateClientListenChoice, sut.getState()) + assert.Equal(s.T(), SmeProtHStateClientListenChoice, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -39,7 +39,7 @@ func (s *ProClientSuite) Test_Init() { func (s *ProClientSuite) Test_ListenChoice() { sut, data := initTest(s.role) - sut.setState(smeProtHStateClientListenChoice) + sut.setState(SmeProtHStateClientListenChoice, nil) protMsg := model.MessageProtocolHandshake{ MessageProtocolHandshake: model.MessageProtocolHandshakeType{ @@ -60,7 +60,7 @@ func (s *ProClientSuite) Test_ListenChoice() { assert.Equal(s.T(), false, sut.handshakeTimerRunning) // state goes directly from smeProtHStateClientOk to smePinStateCheckInit to smePinStateCheckListen - assert.Equal(s.T(), smePinStateCheckListen, sut.getState()) + assert.Equal(s.T(), SmePinStateCheckListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_prot_server_test.go b/ship/hs_prot_server_test.go index 0e6fcc64..78fca7eb 100644 --- a/ship/hs_prot_server_test.go +++ b/ship/hs_prot_server_test.go @@ -24,14 +24,14 @@ func (s *ProServerSuite) BeforeTest(suiteName, testName string) { func (s *ProServerSuite) Test_Init() { sut, data := initTest(s.role) - sut.setState(smeHelloStateOk) + sut.setState(SmeHelloStateOk, nil) sut.handleState(false, nil) assert.Equal(s.T(), true, sut.handshakeTimerRunning) // the state goes from smeHelloStateOk to smeProtHStateServerInit to smeProtHStateServerListenProposal - assert.Equal(s.T(), smeProtHStateServerListenProposal, sut.getState()) + assert.Equal(s.T(), SmeProtHStateServerListenProposal, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -40,7 +40,7 @@ func (s *ProServerSuite) Test_Init() { func (s *ProServerSuite) Test_ListenProposal() { sut, data := initTest(s.role) - sut.setState(smeProtHStateServerListenProposal) + sut.setState(SmeProtHStateServerListenProposal, nil) protMsg := model.MessageProtocolHandshake{ MessageProtocolHandshake: model.MessageProtocolHandshakeType{ @@ -60,7 +60,7 @@ func (s *ProServerSuite) Test_ListenProposal() { assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), smeProtHStateServerListenConfirm, sut.getState()) + assert.Equal(s.T(), SmeProtHStateServerListenConfirm, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -69,7 +69,7 @@ func (s *ProServerSuite) Test_ListenProposal() { func (s *ProServerSuite) Test_ListenConfirm() { sut, data := initTest(s.role) - sut.setState(smeProtHStateServerListenConfirm) + sut.setState(SmeProtHStateServerListenConfirm, nil) protMsg := model.MessageProtocolHandshake{ MessageProtocolHandshake: model.MessageProtocolHandshakeType{ @@ -90,7 +90,7 @@ func (s *ProServerSuite) Test_ListenConfirm() { assert.Equal(s.T(), false, sut.handshakeTimerRunning) // state smeProtHStateServerOk directly goes to smePinStateCheckInit to smePinStateCheckListen - assert.Equal(s.T(), smePinStateCheckListen, sut.getState()) + assert.Equal(s.T(), SmePinStateCheckListen, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/mock_types.go b/ship/mock_types.go new file mode 100644 index 00000000..24c2cb96 --- /dev/null +++ b/ship/mock_types.go @@ -0,0 +1,221 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/enbility/eebus-go/ship (interfaces: ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider) + +// Package ship is a generated GoMock package. +package ship + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockShipDataConnection is a mock of ShipDataConnection interface. +type MockShipDataConnection struct { + ctrl *gomock.Controller + recorder *MockShipDataConnectionMockRecorder +} + +// MockShipDataConnectionMockRecorder is the mock recorder for MockShipDataConnection. +type MockShipDataConnectionMockRecorder struct { + mock *MockShipDataConnection +} + +// NewMockShipDataConnection creates a new mock instance. +func NewMockShipDataConnection(ctrl *gomock.Controller) *MockShipDataConnection { + mock := &MockShipDataConnection{ctrl: ctrl} + mock.recorder = &MockShipDataConnectionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockShipDataConnection) EXPECT() *MockShipDataConnectionMockRecorder { + return m.recorder +} + +// CloseDataConnection mocks base method. +func (m *MockShipDataConnection) CloseDataConnection(arg0 int, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "CloseDataConnection", arg0, arg1) +} + +// CloseDataConnection indicates an expected call of CloseDataConnection. +func (mr *MockShipDataConnectionMockRecorder) CloseDataConnection(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseDataConnection", reflect.TypeOf((*MockShipDataConnection)(nil).CloseDataConnection), arg0, arg1) +} + +// InitDataProcessing mocks base method. +func (m *MockShipDataConnection) InitDataProcessing(arg0 ShipDataProcessing) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "InitDataProcessing", arg0) +} + +// InitDataProcessing indicates an expected call of InitDataProcessing. +func (mr *MockShipDataConnectionMockRecorder) InitDataProcessing(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitDataProcessing", reflect.TypeOf((*MockShipDataConnection)(nil).InitDataProcessing), arg0) +} + +// IsDataConnectionClosed mocks base method. +func (m *MockShipDataConnection) IsDataConnectionClosed() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsDataConnectionClosed") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsDataConnectionClosed indicates an expected call of IsDataConnectionClosed. +func (mr *MockShipDataConnectionMockRecorder) IsDataConnectionClosed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDataConnectionClosed", reflect.TypeOf((*MockShipDataConnection)(nil).IsDataConnectionClosed)) +} + +// WriteMessageToDataConnection mocks base method. +func (m *MockShipDataConnection) WriteMessageToDataConnection(arg0 []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WriteMessageToDataConnection", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// WriteMessageToDataConnection indicates an expected call of WriteMessageToDataConnection. +func (mr *MockShipDataConnectionMockRecorder) WriteMessageToDataConnection(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteMessageToDataConnection", reflect.TypeOf((*MockShipDataConnection)(nil).WriteMessageToDataConnection), arg0) +} + +// MockShipDataProcessing is a mock of ShipDataProcessing interface. +type MockShipDataProcessing struct { + ctrl *gomock.Controller + recorder *MockShipDataProcessingMockRecorder +} + +// MockShipDataProcessingMockRecorder is the mock recorder for MockShipDataProcessing. +type MockShipDataProcessingMockRecorder struct { + mock *MockShipDataProcessing +} + +// NewMockShipDataProcessing creates a new mock instance. +func NewMockShipDataProcessing(ctrl *gomock.Controller) *MockShipDataProcessing { + mock := &MockShipDataProcessing{ctrl: ctrl} + mock.recorder = &MockShipDataProcessingMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockShipDataProcessing) EXPECT() *MockShipDataProcessingMockRecorder { + return m.recorder +} + +// HandleIncomingShipMessage mocks base method. +func (m *MockShipDataProcessing) HandleIncomingShipMessage(arg0 []byte) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HandleIncomingShipMessage", arg0) +} + +// HandleIncomingShipMessage indicates an expected call of HandleIncomingShipMessage. +func (mr *MockShipDataProcessingMockRecorder) HandleIncomingShipMessage(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleIncomingShipMessage", reflect.TypeOf((*MockShipDataProcessing)(nil).HandleIncomingShipMessage), arg0) +} + +// ReportConnectionError mocks base method. +func (m *MockShipDataProcessing) ReportConnectionError(arg0 error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReportConnectionError", arg0) +} + +// ReportConnectionError indicates an expected call of ReportConnectionError. +func (mr *MockShipDataProcessingMockRecorder) ReportConnectionError(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportConnectionError", reflect.TypeOf((*MockShipDataProcessing)(nil).ReportConnectionError), arg0) +} + +// MockShipServiceDataProvider is a mock of ShipServiceDataProvider interface. +type MockShipServiceDataProvider struct { + ctrl *gomock.Controller + recorder *MockShipServiceDataProviderMockRecorder +} + +// MockShipServiceDataProviderMockRecorder is the mock recorder for MockShipServiceDataProvider. +type MockShipServiceDataProviderMockRecorder struct { + mock *MockShipServiceDataProvider +} + +// NewMockShipServiceDataProvider creates a new mock instance. +func NewMockShipServiceDataProvider(ctrl *gomock.Controller) *MockShipServiceDataProvider { + mock := &MockShipServiceDataProvider{ctrl: ctrl} + mock.recorder = &MockShipServiceDataProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockShipServiceDataProvider) EXPECT() *MockShipServiceDataProviderMockRecorder { + return m.recorder +} + +// AllowWaitingForTrust mocks base method. +func (m *MockShipServiceDataProvider) AllowWaitingForTrust(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AllowWaitingForTrust", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. +func (mr *MockShipServiceDataProviderMockRecorder) AllowWaitingForTrust(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockShipServiceDataProvider)(nil).AllowWaitingForTrust), arg0) +} + +// HandleConnectionClosed mocks base method. +func (m *MockShipServiceDataProvider) HandleConnectionClosed(arg0 *ShipConnection, arg1 bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HandleConnectionClosed", arg0, arg1) +} + +// HandleConnectionClosed indicates an expected call of HandleConnectionClosed. +func (mr *MockShipServiceDataProviderMockRecorder) HandleConnectionClosed(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleConnectionClosed", reflect.TypeOf((*MockShipServiceDataProvider)(nil).HandleConnectionClosed), arg0, arg1) +} + +// HandleShipHandshakeStateUpdate mocks base method. +func (m *MockShipServiceDataProvider) HandleShipHandshakeStateUpdate(arg0 string, arg1 ShipState) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HandleShipHandshakeStateUpdate", arg0, arg1) +} + +// HandleShipHandshakeStateUpdate indicates an expected call of HandleShipHandshakeStateUpdate. +func (mr *MockShipServiceDataProviderMockRecorder) HandleShipHandshakeStateUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleShipHandshakeStateUpdate", reflect.TypeOf((*MockShipServiceDataProvider)(nil).HandleShipHandshakeStateUpdate), arg0, arg1) +} + +// IsRemoteServiceForSKIPaired mocks base method. +func (m *MockShipServiceDataProvider) IsRemoteServiceForSKIPaired(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsRemoteServiceForSKIPaired", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsRemoteServiceForSKIPaired indicates an expected call of IsRemoteServiceForSKIPaired. +func (mr *MockShipServiceDataProviderMockRecorder) IsRemoteServiceForSKIPaired(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsRemoteServiceForSKIPaired", reflect.TypeOf((*MockShipServiceDataProvider)(nil).IsRemoteServiceForSKIPaired), arg0) +} + +// ReportServiceShipID mocks base method. +func (m *MockShipServiceDataProvider) ReportServiceShipID(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReportServiceShipID", arg0, arg1) +} + +// ReportServiceShipID indicates an expected call of ReportServiceShipID. +func (mr *MockShipServiceDataProviderMockRecorder) ReportServiceShipID(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportServiceShipID", reflect.TypeOf((*MockShipServiceDataProvider)(nil).ReportServiceShipID), arg0, arg1) +} diff --git a/ship/types.go b/ship/types.go index 6b72026f..5303185e 100644 --- a/ship/types.go +++ b/ship/types.go @@ -46,61 +46,68 @@ const ( timeoutTimerTypeProlongRequestReply ) -type shipMessageExchangeState uint +type ShipState struct { + State ShipMessageExchangeState + Error error +} + +type ShipMessageExchangeState uint const ( // Connection Mode Initialisation (CMI) SHIP 13.4.3 - cmiStateInitStart shipMessageExchangeState = iota - cmiStateClientSend - cmiStateClientWait - cmiStateClientEvaluate - cmiStateServerWait - cmiStateServerEvaluate + CmiStateInitStart ShipMessageExchangeState = iota + CmiStateClientSend + CmiStateClientWait + CmiStateClientEvaluate + CmiStateServerWait + CmiStateServerEvaluate // Connection Data Preparation SHIP 13.4.4 - smeHelloState - smeHelloStateReadyInit - smeHelloStateReadyListen - smeHelloStateReadyTimeout - smeHelloStatePendingInit - smeHelloStatePendingListen - smeHelloStatePendingTimeout - smeHelloStateOk - smeHelloStateAbort + SmeHelloState + SmeHelloStateReadyInit + SmeHelloStateReadyListen + SmeHelloStateReadyTimeout + SmeHelloStatePendingInit + SmeHelloStatePendingListen + SmeHelloStatePendingTimeout + SmeHelloStateOk + SmeHelloStateAbort // Connection State Protocol Handhsake SHIP 13.4.4.2 - smeProtHStateServerInit - smeProtHStateClientInit - smeProtHStateServerListenProposal - smeProtHStateServerListenConfirm - smeProtHStateClientListenChoice - smeProtHStateTimeout - smeProtHStateClientOk - smeProtHStateServerOk + SmeProtHStateServerInit + SmeProtHStateClientInit + SmeProtHStateServerListenProposal + SmeProtHStateServerListenConfirm + SmeProtHStateClientListenChoice + SmeProtHStateTimeout + SmeProtHStateClientOk + SmeProtHStateServerOk // Connection PIN State 13.4.5 - smePinStateCheckInit - smePinStateCheckListen - smePinStateCheckError - smePinStateCheckBusyInit - smePinStateCheckBusyWait - smePinStateCheckOk - smePinStateAskInit - smePinStateAskProcess - smePinStateAskRestricted - smePinStateAskOk + SmePinStateCheckInit + SmePinStateCheckListen + SmePinStateCheckError + SmePinStateCheckBusyInit + SmePinStateCheckBusyWait + SmePinStateCheckOk + SmePinStateAskInit + SmePinStateAskProcess + SmePinStateAskRestricted + SmePinStateAskOk // ConnectionAccess Methods Identification 13.4.6 - smeAccessMethodsRequest + SmeAccessMethodsRequest // Handshake approved on both ends - smeApproved + SmeApproved // Handshake process is successfully completed - smeComplete + SmeComplete // Handshake ended with an error - smeError + SmeError ) var shipInit []byte = []byte{model.MsgTypeInit, 0x00} +//go:generate mockgen -destination=mock_types.go -package=ship github.com/enbility/eebus-go/ship ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider + // interface for handling the actual remote device data connection // // implemented by websocketConnection, used by ShipConnection @@ -112,10 +119,10 @@ type ShipDataConnection interface { WriteMessageToDataConnection([]byte) error // close the data connection - CloseDataConnection() + CloseDataConnection(closeCode int, reason string) - // report if the data connection is closed - IsDataConnectionClosed() bool + // report if the data connection is closed and the error if availab le + IsDataConnectionClosed() (bool, error) } // interface for handling incoming data @@ -142,4 +149,10 @@ type ShipServiceDataProvider interface { // report the ship ID provided during the handshake ReportServiceShipID(string, string) + + // check if the user is still able to trust the connection + AllowWaitingForTrust(string) bool + + // report the updated SHIP handshake state and optional error message for a SKI + HandleShipHandshakeStateUpdate(string, ShipState) } diff --git a/ship/websocket.go b/ship/websocket.go index 95f0325a..221cb1dc 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -29,6 +29,9 @@ type websocketConnection struct { // internal handling of closed connections connectionClosed bool + // the error message received for the closed connection + connectionClosedError error + remoteSki string muxConnClosed sync.Mutex @@ -39,25 +42,37 @@ type websocketConnection struct { // create a new websocket based shipDataProcessing implementation func NewWebsocketConnection(conn *websocket.Conn, remoteSki string) *websocketConnection { return &websocketConnection{ - conn: conn, - remoteSki: remoteSki, + conn: conn, + remoteSki: remoteSki, + connectionClosedError: nil, } } -// check if the websocket connection is closed -func (w *websocketConnection) isConnClosed() bool { +// sets the error message for the closed connection +func (w *websocketConnection) setConnClosedError(err error) { w.muxConnClosed.Lock() defer w.muxConnClosed.Unlock() - return w.connectionClosed + w.connectionClosed = true + + if err != nil { + w.connectionClosedError = err + } +} + +func (w *websocketConnection) connClosedError() error { + w.muxConnClosed.Lock() + defer w.muxConnClosed.Unlock() + + return w.connectionClosedError } // check if the websocket connection is closed -func (w *websocketConnection) setConnClosed() { +func (w *websocketConnection) isConnClosed() bool { w.muxConnClosed.Lock() defer w.muxConnClosed.Unlock() - w.connectionClosed = true + return w.connectionClosed } func (w *websocketConnection) run() { @@ -95,6 +110,8 @@ func (w *websocketConnection) writeShipPump() { if err := w.conn.WriteMessage(websocket.BinaryMessage, message); err != nil { logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) + w.setConnClosedError(err) + w.dataProcessing.ReportConnectionError(err) return } @@ -116,6 +133,8 @@ func (w *websocketConnection) writeShipPump() { _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) if err := w.conn.WriteMessage(websocket.PingMessage, nil); err != nil { logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) + w.setConnClosedError(err) + w.dataProcessing.ReportConnectionError(err) return } } @@ -136,6 +155,7 @@ func (w *websocketConnection) readShipPump() { if err != nil { logging.Log.Debug(w.remoteSki, "websocket read error: ", err) w.close() + w.setConnClosedError(err) w.dataProcessing.ReportConnectionError(err) return } @@ -183,7 +203,7 @@ func (w *websocketConnection) close() { return } - w.setConnClosed() + w.setConnClosedError(nil) w.muxShipWrite.Lock() @@ -231,13 +251,23 @@ func (w *websocketConnection) WriteMessageToDataConnection(message []byte) error } // shutdown the connection and all internals -func (w *websocketConnection) CloseDataConnection() { +func (w *websocketConnection) CloseDataConnection(closeCode int, reason string) { if !w.isConnClosed() { + if reason != "" { + _ = w.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, reason)) + } w.close() } } // return if the connection is closed -func (w *websocketConnection) IsDataConnectionClosed() bool { - return w.isConnClosed() +func (w *websocketConnection) IsDataConnectionClosed() (bool, error) { + isClosed := w.isConnClosed() + err := w.connClosedError() + + if isClosed && err == nil { + err = errors.New("connection is closed") + } + + return isClosed, err } diff --git a/spine/mocks/Sender.go b/spine/mocks/Sender.go index 3d7b1a71..a04d19ac 100644 --- a/spine/mocks/Sender.go +++ b/spine/mocks/Sender.go @@ -1,11 +1,12 @@ -// Code generated by mockery v2.14.1. DO NOT EDIT. +// Code generated by mockery v2.26.1. DO NOT EDIT. package mocks import ( - spine "github.com/enbility/eebus-go/spine" model "github.com/enbility/eebus-go/spine/model" mock "github.com/stretchr/testify/mock" + + spine "github.com/enbility/eebus-go/spine" ) // Sender is an autogenerated mock type for the Sender type @@ -18,6 +19,10 @@ func (_m *Sender) Bind(senderAddress *model.FeatureAddressType, destinationAddre ret := _m.Called(senderAddress, destinationAddress, serverFeatureType) var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) (*model.MsgCounterType, error)); ok { + return rf(senderAddress, destinationAddress, serverFeatureType) + } if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) *model.MsgCounterType); ok { r0 = rf(senderAddress, destinationAddress, serverFeatureType) } else { @@ -26,7 +31,6 @@ func (_m *Sender) Bind(senderAddress *model.FeatureAddressType, destinationAddre } } - var r1 error if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) error); ok { r1 = rf(senderAddress, destinationAddress, serverFeatureType) } else { @@ -41,6 +45,10 @@ func (_m *Sender) Notify(senderAddress *model.FeatureAddressType, destinationAdd ret := _m.Called(senderAddress, destinationAddress, cmd) var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) (*model.MsgCounterType, error)); ok { + return rf(senderAddress, destinationAddress, cmd) + } if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) *model.MsgCounterType); ok { r0 = rf(senderAddress, destinationAddress, cmd) } else { @@ -49,7 +57,6 @@ func (_m *Sender) Notify(senderAddress *model.FeatureAddressType, destinationAdd } } - var r1 error if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) error); ok { r1 = rf(senderAddress, destinationAddress, cmd) } else { @@ -78,6 +85,10 @@ func (_m *Sender) Request(cmdClassifier model.CmdClassifierType, senderAddress * ret := _m.Called(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(model.CmdClassifierType, *model.FeatureAddressType, *model.FeatureAddressType, bool, []model.CmdType) (*model.MsgCounterType, error)); ok { + return rf(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) + } if rf, ok := ret.Get(0).(func(model.CmdClassifierType, *model.FeatureAddressType, *model.FeatureAddressType, bool, []model.CmdType) *model.MsgCounterType); ok { r0 = rf(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) } else { @@ -86,7 +97,6 @@ func (_m *Sender) Request(cmdClassifier model.CmdClassifierType, senderAddress * } } - var r1 error if rf, ok := ret.Get(1).(func(model.CmdClassifierType, *model.FeatureAddressType, *model.FeatureAddressType, bool, []model.CmdType) error); ok { r1 = rf(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) } else { @@ -129,6 +139,10 @@ func (_m *Sender) Subscribe(senderAddress *model.FeatureAddressType, destination ret := _m.Called(senderAddress, destinationAddress, serverFeatureType) var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) (*model.MsgCounterType, error)); ok { + return rf(senderAddress, destinationAddress, serverFeatureType) + } if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) *model.MsgCounterType); ok { r0 = rf(senderAddress, destinationAddress, serverFeatureType) } else { @@ -137,7 +151,6 @@ func (_m *Sender) Subscribe(senderAddress *model.FeatureAddressType, destination } } - var r1 error if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) error); ok { r1 = rf(senderAddress, destinationAddress, serverFeatureType) } else { @@ -152,6 +165,10 @@ func (_m *Sender) Write(senderAddress *model.FeatureAddressType, destinationAddr ret := _m.Called(senderAddress, destinationAddress, cmd) var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) (*model.MsgCounterType, error)); ok { + return rf(senderAddress, destinationAddress, cmd) + } if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) *model.MsgCounterType); ok { r0 = rf(senderAddress, destinationAddress, cmd) } else { @@ -160,7 +177,6 @@ func (_m *Sender) Write(senderAddress *model.FeatureAddressType, destinationAddr } } - var r1 error if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) error); ok { r1 = rf(senderAddress, destinationAddress, cmd) } else { From 0597cf2fcfcd82058d1072a223e6aeea58e73ca5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 24 May 2023 10:59:36 +0200 Subject: [PATCH 008/240] Simplify --- service/hub.go | 9 +++++---- service/types.go | 12 ------------ ship/connection.go | 6 +----- ship/connection_test.go | 2 +- ship/hs_helper_test.go | 2 +- 5 files changed, 8 insertions(+), 23 deletions(-) diff --git a/service/hub.go b/service/hub.go index 97096fc9..b6aeef52 100644 --- a/service/hub.go +++ b/service/hub.go @@ -165,8 +165,9 @@ func (h *connectionsHub) HandleConnectionClosed(connection *ship.ShipConnection, h.serviceProvider.RemoteSKIDisconnected(connection.RemoteSKI) - // Do not automatically reconnect if handshake failed and autoRetry is false - if !handshakeCompleted && !h.configuration.autoRetryShipHandshake { + // Do not automatically reconnect if handshake failed and not already paired + remoteService := h.serviceForSKI(connection.RemoteSKI) + if !handshakeCompleted && !remoteService.Paired { return } @@ -473,7 +474,7 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI) shipConnection := ship.NewConnectionHandler(h, dataHandler, h.spineLocalDevice, ship.ShipRoleServer, - h.localService.ShipID, remoteService.SKI, remoteService.ShipID, h.configuration.autoRetryShipHandshake) + h.localService.ShipID, remoteService.SKI, remoteService.ShipID) shipConnection.Run() h.registerConnection(shipConnection) @@ -538,7 +539,7 @@ func (h *connectionsHub) connectFoundService(remoteService *ServiceDetails, host dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI) shipConnection := ship.NewConnectionHandler(h, dataHandler, h.spineLocalDevice, ship.ShipRoleClient, - h.localService.ShipID, remoteService.SKI, remoteService.ShipID, h.configuration.autoRetryShipHandshake) + h.localService.ShipID, remoteService.SKI, remoteService.ShipID) shipConnection.Run() h.registerConnection(shipConnection) diff --git a/service/types.go b/service/types.go index 249ccc32..97209355 100644 --- a/service/types.go +++ b/service/types.go @@ -133,11 +133,6 @@ type Configuration struct { // This is useful when e.g. power values are not available and therefor // need to be calculated using the current values voltage float64 - - // Automatically retry a connection that ends in a SHIP abort, error or timeout - // Defaut: false - // Useful if the service does not have a pairing UI - autoRetryShipHandshake bool } // Setup a Configuration with the required parameters @@ -191,13 +186,6 @@ func NewConfiguration( return configuration, nil } -// defines if SHIP handshakes not completing successfully should -// also be considered as disconnections that need to be retried automatically -// default: false -func (s *Configuration) SetAutoRetryShipHandshake(retry bool) { - s.autoRetryShipHandshake = retry -} - // define an alternative mDNS and SHIP identifier // usually this is only used when no deviceCode is available or identical to the brand // if this is not set, generated identifier is used diff --git a/ship/connection.go b/ship/connection.go index 29a9be10..f30acc92 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -38,9 +38,6 @@ type ShipConnection struct { // the handler for sending messages on the data connection DataHandler ShipDataConnection - // defines if connections where handshakes do not complete successfully should be automatically retried as closed connections - autoRetryHandshake bool - // The current SHIP state smeState ShipMessageExchangeState @@ -69,7 +66,7 @@ type ShipConnection struct { mux sync.Mutex } -func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler ShipDataConnection, deviceLocalCon spine.DeviceLocalConnection, role shipRole, localShipID, remoteSki, remoteShipId string, retry bool) *ShipConnection { +func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler ShipDataConnection, deviceLocalCon spine.DeviceLocalConnection, role shipRole, localShipID, remoteSki, remoteShipId string) *ShipConnection { ship := &ShipConnection{ serviceDataProvider: dataProvider, deviceLocalCon: deviceLocalCon, @@ -80,7 +77,6 @@ func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler Ship DataHandler: dataHandler, smeState: CmiStateInitStart, smeError: nil, - autoRetryHandshake: retry, } ship.handshakeTimerStopChan = make(chan struct{}) diff --git a/ship/connection_test.go b/ship/connection_test.go index b1f95fce..d87201cd 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -44,7 +44,7 @@ func (s *ConnectionSuite) BeforeTest(suiteName, testName string) { s.shipDataConn.EXPECT().WriteMessageToDataConnection(gomock.Any()).DoAndReturn(func(message []byte) error { s.sentMessage = message; return nil }).AnyTimes() s.shipDataConn.EXPECT().IsDataConnectionClosed().DoAndReturn(func() (bool, error) { return false, nil }).AnyTimes() - s.sut = NewConnectionHandler(s.shipDataProvider, s.shipDataConn, localDevice, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID", false) + s.sut = NewConnectionHandler(s.shipDataProvider, s.shipDataConn, localDevice, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID") } func (s *ConnectionSuite) TestSendShipModel() { diff --git a/ship/hs_helper_test.go b/ship/hs_helper_test.go index e69760b2..3a986725 100644 --- a/ship/hs_helper_test.go +++ b/ship/hs_helper_test.go @@ -55,7 +55,7 @@ func initTest(role shipRole) (*ShipConnection, *dataHandlerTest) { "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart) dataHandler := &dataHandlerTest{} - conhandler := NewConnectionHandler(dataHandler, dataHandler, localDevice, role, "LocalShipID", "RemoveDevice", "RemoteShipID", false) + conhandler := NewConnectionHandler(dataHandler, dataHandler, localDevice, role, "LocalShipID", "RemoveDevice", "RemoteShipID") return conhandler, dataHandler } From 257b1f0f6f21ccb43c948c754fe58509e9808a33 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 27 May 2023 09:07:52 +0200 Subject: [PATCH 009/240] Improve description --- service/hub.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/hub.go b/service/hub.go index b6aeef52..906371a5 100644 --- a/service/hub.go +++ b/service/hub.go @@ -610,8 +610,8 @@ func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { return service } -// Sets the SKI to be paired -// Should be used if for services which finalized the pairing process and where +// Sets the SKI as being paired or not +// Should be used for services which completed the pairing process and where // stored as having the process completed func (h *connectionsHub) EnablePairingForSKI(ski string, enable bool) { service := h.serviceForSKI(ski) From 6225679518ba8d90d54ee992805e3b6d43d02e35 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 27 May 2023 09:08:19 +0200 Subject: [PATCH 010/240] Fix pairing state not properly updated --- service/hub.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/service/hub.go b/service/hub.go index 906371a5..a1ffa702 100644 --- a/service/hub.go +++ b/service/hub.go @@ -233,6 +233,10 @@ func (h *connectionsHub) AllowWaitingForTrust(ski string) bool { func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.ShipState) { service := h.serviceForSKI(ski) + if state.State == ship.SmeComplete { + h.EnablePairingForSKI(ski, true) + } + pairingState := h.mapShipMessageExchangeState(state.State, ski) if state.Error != nil && state.Error != ErrConnectionNotFound { pairingState = PairingStateError From 9b763a45368b929de5f2578ae09d74b1750b2bfd Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 27 May 2023 10:02:42 +0200 Subject: [PATCH 011/240] Fix SHIP close handling --- ship/connection.go | 3 ++- ship/hs_access_test.go | 2 +- ship/hs_init_client_test.go | 6 +++--- ship/hs_init_server_test.go | 4 ++-- ship/hs_pin_test.go | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ship/connection.go b/ship/connection.go index f30acc92..46b128c3 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -129,7 +129,8 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { c.removeRemoteDeviceConnection() - if safe && c.getState() > CmiStateInitStart { + // this may not be used for Connection Data Exchange is entered! + if safe && c.getState() == SmeComplete { // SHIP 13.4.7: Connection Termination Announce closeMessage := model.ConnectionClose{ ConnectionClose: model.ConnectionCloseType{ diff --git a/ship/hs_access_test.go b/ship/hs_access_test.go index edc670e3..7223bc17 100644 --- a/ship/hs_access_test.go +++ b/ship/hs_access_test.go @@ -92,7 +92,7 @@ func (s *AccessSuite) Test_Methods_WrongShipID() { assert.Equal(s.T(), false, sut.handshakeTimerRunning) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } diff --git a/ship/hs_init_client_test.go b/ship/hs_init_client_test.go index 01b89439..7db506e5 100644 --- a/ship/hs_init_client_test.go +++ b/ship/hs_init_client_test.go @@ -66,7 +66,7 @@ func (s *InitClientSuite) Test_ClientWait_Timeout() { sut.handleState(true, nil) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) assert.Equal(s.T(), data.handleConnectionClosedInvoked, true) shutdownTest(sut) @@ -80,7 +80,7 @@ func (s *InitClientSuite) Test_ClientWait_InvalidMsgType() { sut.handleState(false, []byte{0x05, 0x00}) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } @@ -93,7 +93,7 @@ func (s *InitClientSuite) Test_ClientWait_InvalidData() { sut.handleState(false, []byte{model.MsgTypeInit, 0x05}) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } diff --git a/ship/hs_init_server_test.go b/ship/hs_init_server_test.go index 4ec129a7..fdf0e573 100644 --- a/ship/hs_init_server_test.go +++ b/ship/hs_init_server_test.go @@ -64,7 +64,7 @@ func (s *InitServerSuite) Test_ServerWait_InvalidMsgType() { sut.handleState(false, []byte{0x05, 0x00}) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } @@ -77,7 +77,7 @@ func (s *InitServerSuite) Test_ServerWait_InvalidData() { sut.handleState(false, []byte{model.MsgTypeInit, 0x05}) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } diff --git a/ship/hs_pin_test.go b/ship/hs_pin_test.go index 4f6a1b60..90f2be32 100644 --- a/ship/hs_pin_test.go +++ b/ship/hs_pin_test.go @@ -70,7 +70,7 @@ func (s *PinSuite) Test_CheckListen_Required() { assert.Equal(s.T(), false, sut.handshakeTimerRunning) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } @@ -93,7 +93,7 @@ func (s *PinSuite) Test_CheckListen_Optional() { assert.Equal(s.T(), false, sut.handshakeTimerRunning) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } @@ -116,7 +116,7 @@ func (s *PinSuite) Test_CheckListen_Ok() { assert.Equal(s.T(), false, sut.handshakeTimerRunning) assert.Equal(s.T(), SmeError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } From 198df3531f43279d7087c34a644e1db4bc9da38a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 27 May 2023 21:39:56 +0200 Subject: [PATCH 012/240] Do not delay connection on pairing --- service/hub.go | 59 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/service/hub.go b/service/hub.go index a1ffa702..ff3a5191 100644 --- a/service/hub.go +++ b/service/hub.go @@ -714,44 +714,57 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry MdnsEn } h.setConnectionAttemptRunning(ski, true) + counter, duration := h.getConnectionInitiationDelayTime(ski) + service := h.serviceForSKI(ski) + if service.PairingDetail.State == PairingStateQueued { + go h.prepareConnectionInitation(ski, counter, entry) + return + } + logging.Log.Debugf("delaying connection to %s by %s to minimize double connection probability", ski, duration) + // we do not stop this thread and just let the timer run out // otherwise we would need a stop channel for each ski go func() { // wait <-time.After(duration) - h.setConnectionAttemptRunning(ski, false) + h.prepareConnectionInitation(ski, counter, entry) + }() +} - // check if the remoteService still exists - service := h.serviceForSKI(ski) +// invoked by coordinateConnectionInitations either with a delay or directly +// when initating a pairing process +func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, entry MdnsEntry) { + h.setConnectionAttemptRunning(ski, false) - // check if the current counter is still the same, otherwise this counter is irrelevant - currentCounter, exists := h.getCurrentConnectionAttemptCounter(ski) - if !exists || currentCounter != counter { - return - } + // check if the remoteService still exists + service := h.serviceForSKI(ski) - // connection attempt is not relevant if the device is no longer paired - // or it is not queued for pairing - pairingState := h.serviceForSKI(ski).PairingDetail.State - if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != PairingStateQueued { - return - } + // check if the current counter is still the same, otherwise this counter is irrelevant + currentCounter, exists := h.getCurrentConnectionAttemptCounter(ski) + if !exists || currentCounter != counter { + return + } - // connection attempt is not relevant if the device is already connected - if h.isSkiConnected(ski) { - return - } + // connection attempt is not relevant if the device is no longer paired + // or it is not queued for pairing + pairingState := h.serviceForSKI(ski).PairingDetail.State + if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != PairingStateQueued { + return + } - // now initiate the connection - if success := h.initateConnection(service, entry); !success { - h.checkRestartMdnsSearch() - } + // connection attempt is not relevant if the device is already connected + if h.isSkiConnected(ski) { + return + } - }() + // now initiate the connection + if success := h.initateConnection(service, entry); !success { + h.checkRestartMdnsSearch() + } } // attempt to establish a connection to a remote service From a8f803043c4af4f0c141eff2004df1c59e0a7aa5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 27 May 2023 21:40:27 +0200 Subject: [PATCH 013/240] Support and handle Hello Abort --- service/hub.go | 27 +++++++++++++++++++++------ ship/connection.go | 31 +++++++++++++++++++++++++++++-- ship/handshake.go | 12 +++++++++--- ship/hs_hello.go | 8 +++++--- ship/hs_hello_test.go | 14 +++++++------- ship/types.go | 2 ++ 6 files changed, 73 insertions(+), 21 deletions(-) diff --git a/service/hub.go b/service/hub.go index ff3a5191..59adaa83 100644 --- a/service/hub.go +++ b/service/hub.go @@ -291,6 +291,8 @@ func (h *connectionsHub) mapShipMessageExchangeState(state ship.ShipMessageExcha connState = PairingStateInProgress case ship.SmeHelloStatePendingInit, ship.SmeHelloStatePendingListen, ship.SmeHelloStatePendingTimeout: connState = PairingStateReceived + case ship.SmeHelloStateAbort, ship.SmeHelloStateAbortDone: + connState = PairingStateNone case ship.SmePinStateCheckInit, ship.SmePinStateCheckListen, ship.SmePinStateCheckError, ship.SmePinStateCheckBusyInit, ship.SmePinStateCheckBusyWait, ship.SmePinStateCheckOk, ship.SmePinStateAskInit, ship.SmePinStateAskProcess, ship.SmePinStateAskRestricted, @@ -620,6 +622,21 @@ func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { func (h *connectionsHub) EnablePairingForSKI(ski string, enable bool) { service := h.serviceForSKI(ski) service.Paired = enable + + if enable { + return + } + + h.removeConnectionAttemptCounter(ski) + + service.PairingDetail.State = PairingStateNone + service.Paired = false + + h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) + + if existingC := h.connectionForSKI(ski); existingC != nil { + existingC.CloseConnection(true, "pairing cancelled") + } } // Triggers the pairing process for a SKI @@ -649,17 +666,15 @@ func (h *connectionsHub) InitiatePairingWithSKI(ski string) { func (h *connectionsHub) CancelPairingWithSKI(ski string) { h.removeConnectionAttemptCounter(ski) + if existingC := h.connectionForSKI(ski); existingC != nil { + existingC.AbortPendingHandshake() + } + service := h.serviceForSKI(ski) service.PairingDetail.State = PairingStateNone service.Paired = false - h.removeConnectionAttemptCounter(ski) - h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) - - if existingC := h.connectionForSKI(ski); existingC != nil { - existingC.CloseConnection(true, "pairing cancelled") - } } // Process reported mDNS services diff --git a/ship/connection.go b/ship/connection.go index 46b128c3..f1f756eb 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -117,6 +117,22 @@ func (c *ShipConnection) ApprovePendingHandshake() { c.handleState(false, nil) } +// invoked when pairing for a pending request is denied +func (c *ShipConnection) AbortPendingHandshake() { + state := c.getState() + if state != SmeHelloStatePendingListen && state != SmeHelloStateReadyListen { + // TODO: what to do if the state is differnet? + + return + } + + // TODO: Move this into hs_hello.go and add tests + + c.stopHandshakeTimer() + c.setState(SmeHelloStateAbort, nil) + c.handleState(false, nil) +} + // report removing a connection func (c *ShipConnection) removeRemoteDeviceConnection() { c.deviceLocalCon.RemoveRemoteDeviceConnection(c.RemoteSKI) @@ -146,8 +162,13 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { } } - c.DataHandler.CloseDataConnection(4495, "Timeout") - c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeComplete) + c.DataHandler.CloseDataConnection(4001, reason) + + // handshake is completed if approved or aborted + state := c.getState() + handshakeEnd := state == SmeComplete || state == SmeHelloStateAbort || state == SmeHelloStateAbortDone + + c.serviceDataProvider.HandleConnectionClosed(c, handshakeEnd) }) } @@ -210,6 +231,12 @@ func (c *ShipConnection) hasSpineDatagram(message []byte) bool { // the websocket data connection was closed from remote func (c *ShipConnection) ReportConnectionError(err error) { + // if the handshake is aborted, a closed connection is no error + currentState := c.getState() + if currentState == SmeHelloStateAbort || currentState == SmeHelloStateAbortDone { + return + } + c.setState(SmeError, err) c.CloseConnection(false, "") diff --git a/ship/handshake.go b/ship/handshake.go index cd1aff72..0fa50202 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -29,11 +29,11 @@ func (c *ShipConnection) handleShipMessage(timeout bool, message []byte) { <-time.After(500 * time.Millisecond) // - c.DataHandler.CloseDataConnection(4495, "timeout") + c.DataHandler.CloseDataConnection(4001, "close") c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeComplete) case model.ConnectionClosePhaseTypeConfirm: // we got a confirmation so close this connection - c.DataHandler.CloseDataConnection(4496, "close") + c.DataHandler.CloseDataConnection(4001, "close") c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeComplete) } @@ -59,7 +59,7 @@ func (c *ShipConnection) setState(newState ShipMessageExchangeState, err error) c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) case SmeHelloStateOk: c.stopHandshakeTimer() - case SmeHelloStateAbort: + case SmeHelloStateAbort, SmeHelloStateAbortDone: c.stopHandshakeTimer() case SmeProtHStateClientListenChoice: c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) @@ -166,6 +166,12 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { case SmeHelloStateAbort: c.handshakeHello_Abort() + case SmeHelloStateAbortDone: + go func() { + time.Sleep(time.Second) + c.CloseConnection(false, "") + }() + // smeProtocol case SmeProtHStateServerListenProposal: diff --git a/ship/hs_hello.go b/ship/hs_hello.go index aa15475a..38efa5c2 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -60,8 +60,9 @@ func (c *ShipConnection) handshakeHello_ReadyListen(message []byte) { // TODO: what to do if this is false? case model.ConnectionHelloPhaseTypeAborted: - c.setState(SmeHelloStateAbort, nil) + c.setState(SmeHelloStateAbortDone, nil) c.handleState(false, nil) + return default: @@ -84,7 +85,8 @@ func (c *ShipConnection) handshakeHello_Abort() { return } - c.CloseConnection(false, "") + c.setState(SmeHelloStateAbortDone, nil) + c.handleState(false, nil) } // SME_HELLO_PENDING_INIT @@ -179,7 +181,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { c.handleState(false, nil) case model.ConnectionHelloPhaseTypeAborted: - c.setState(SmeHelloStateAbort, nil) + c.setState(SmeHelloStateAbortDone, nil) c.handleState(false, nil) return diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go index cdbf1b01..5c798769 100644 --- a/ship/hs_hello_test.go +++ b/ship/hs_hello_test.go @@ -79,7 +79,7 @@ func (s *HelloSuite) Test_ReadyListen_Timeout() { time.Sleep(tHelloInit + time.Second) - assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) + assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -127,8 +127,8 @@ func (s *HelloSuite) Test_ReadyListen_Abort() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } @@ -166,7 +166,7 @@ func (s *HelloSuite) Test_PendingListen_Timeout() { time.Sleep(tHelloInit + time.Second) - assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) + assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -191,7 +191,7 @@ func (s *HelloSuite) Test_PendingListen_ReadyAbort() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) + assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -241,8 +241,8 @@ func (s *HelloSuite) Test_PendingListen_Abort() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateAbort, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) + assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) + assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) } diff --git a/ship/types.go b/ship/types.go index 5303185e..b51bb9e0 100644 --- a/ship/types.go +++ b/ship/types.go @@ -71,6 +71,8 @@ const ( SmeHelloStatePendingTimeout SmeHelloStateOk SmeHelloStateAbort + SmeHelloStateAbortDone + // Connection State Protocol Handhsake SHIP 13.4.4.2 SmeProtHStateServerInit SmeProtHStateClientInit From 0bf2f2a1de6c4af5c4d5350c0ef99bd92adcf5e9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 27 May 2023 22:28:53 +0200 Subject: [PATCH 014/240] Make sure not to concurrently to websocket --- ship/websocket.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ship/websocket.go b/ship/websocket.go index 221cb1dc..75ffa5a3 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -36,6 +36,7 @@ type websocketConnection struct { muxConnClosed sync.Mutex muxShipWrite sync.Mutex + muxConWrite sync.Mutex shutdownOnce sync.Once } @@ -104,11 +105,11 @@ func (w *websocketConnection) writeShipPump() { if !ok { logging.Log.Debug(w.remoteSki, "Ship write channel closed") // The write channel has been closed - _ = w.conn.WriteMessage(websocket.CloseMessage, []byte{}) + _ = w.writeMessage(websocket.CloseMessage, []byte{}) return } - if err := w.conn.WriteMessage(websocket.BinaryMessage, message); err != nil { + if err := w.writeMessage(websocket.BinaryMessage, message); err != nil { logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) w.setConnClosedError(err) w.dataProcessing.ReportConnectionError(err) @@ -131,7 +132,7 @@ func (w *websocketConnection) writeShipPump() { } _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) - if err := w.conn.WriteMessage(websocket.PingMessage, nil); err != nil { + if err := w.writeMessage(websocket.PingMessage, nil); err != nil { logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) w.setConnClosedError(err) w.dataProcessing.ReportConnectionError(err) @@ -250,11 +251,19 @@ func (w *websocketConnection) WriteMessageToDataConnection(message []byte) error return nil } +// make sure websocket Write is only called once at a time +func (w *websocketConnection) writeMessage(messageType int, data []byte) error { + w.muxConWrite.Lock() + defer w.muxConWrite.Unlock() + + return w.conn.WriteMessage(messageType, data) +} + // shutdown the connection and all internals func (w *websocketConnection) CloseDataConnection(closeCode int, reason string) { if !w.isConnClosed() { if reason != "" { - _ = w.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, reason)) + _ = w.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, reason)) } w.close() } From 0f25c3b96be4d0f43becf3995159a8687536f872 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 18:20:54 +0200 Subject: [PATCH 015/240] Multiple improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Multiple variable, states, function namings - Added RemoteDeniedTrust ConnectionState - Ignore read and write websocket errors, if the connection is closed - Various SmeAbort… connection closing improvements --- README.md | 3 ++ cmd/evse/main.go | 4 +- cmd/hems/main.go | 4 +- service/hub.go | 103 +++++++++++++++++++----------------- service/hub_test.go | 4 +- service/mock_hub.go | 2 +- service/service.go | 10 ++-- service/types.go | 39 +++++++------- ship/connection.go | 19 ++++--- ship/handshake.go | 20 ++++--- ship/hs_access.go | 2 +- ship/hs_access_test.go | 4 +- ship/hs_hello.go | 4 +- ship/hs_hello_test.go | 4 +- ship/hs_init_client_test.go | 6 +-- ship/hs_init_server_test.go | 4 +- ship/hs_pin_test.go | 6 +-- ship/types.go | 11 ++-- ship/websocket.go | 10 ++++ 19 files changed, 147 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 62f2718e..a705e47f 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,9 @@ If no certfile or keyfile are provided, they are generated and printed in the co - Double connection handling is not implemented according to SHIP 12.2.2. Instead the connection initiated by the higher SKI will be kept. Much simpler and always works - PIN Verification is _NOT_ supported other than SHIP 13.4.4.3.5.1 _"none"_ PIN state is supported! - Access Methods SHIP 13.4.6 only supports the most basic scenario and only works after PIN verification state is completed. +- Supported registration mechanisms (SHIP 5): + - auto accept (without any interaction mechanism!) + - user verification This approach has been tested with: diff --git a/cmd/evse/main.go b/cmd/evse/main.go index dbe08254..fb3ac77e 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -83,7 +83,7 @@ func (h *evse) run() { h.myService.Start() // defer h.myService.Shutdown() - h.myService.EnablePairingForSKI(remoteSki, true) + h.myService.RegisterRemoteSKI(remoteSki, true) } // EEBUSServiceHandler @@ -97,7 +97,7 @@ func (h *evse) VisibleRemoteServicesUpdated(service *service.EEBUSService, entri func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *evse) ServicePairingDetailUpdate(ski string, detail service.PairingDetail) {} +func (h *evse) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) {} func (h *evse) AllowWaitingForTrust(ski string) bool { return true } diff --git a/cmd/hems/main.go b/cmd/hems/main.go index d20074f3..19a5a0cf 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -83,7 +83,7 @@ func (h *hems) run() { h.myService.Start() // defer h.myService.Shutdown() - h.myService.EnablePairingForSKI(remoteSki, true) + h.myService.RegisterRemoteSKI(remoteSki, true) } // EEBUSServiceHandler @@ -97,7 +97,7 @@ func (h *hems) VisibleRemoteServicesUpdated(service *service.EEBUSService, entri func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *hems) ServicePairingDetailUpdate(ski string, detail service.PairingDetail) {} +func (h *hems) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) {} func (h *hems) AllowWaitingForTrust(ski string) bool { return true } diff --git a/service/hub.go b/service/hub.go index 59adaa83..e2571c76 100644 --- a/service/hub.go +++ b/service/hub.go @@ -60,7 +60,7 @@ type ServiceProvider interface { ServiceShipIDUpdate(ski string, shipID string) // provides the current handshake state for a given SKI - ServicePairingDetailUpdate(ski string, detail PairingDetail) + ServicePairingDetailUpdate(ski string, detail ConnectionStateDetail) // return if the user is still able to trust the connection AllowWaitingForTrust(ski string) bool @@ -143,7 +143,7 @@ var _ ship.ShipServiceDataProvider = (*connectionsHub)(nil) func (h *connectionsHub) IsRemoteServiceForSKIPaired(ski string) bool { service := h.serviceForSKI(ski) - return service.Paired + return service.Trusted } // The connection was closed, we need to clean up @@ -167,7 +167,7 @@ func (h *connectionsHub) HandleConnectionClosed(connection *ship.ShipConnection, // Do not automatically reconnect if handshake failed and not already paired remoteService := h.serviceForSKI(connection.RemoteSKI) - if !handshakeCompleted && !remoteService.Paired { + if !handshakeCompleted && !remoteService.Trusted { return } @@ -180,7 +180,7 @@ func (h *connectionsHub) numberPairedServices() int { h.muxReg.Lock() for _, service := range h.remoteServices { - if service.Paired { + if service.Trusted { amount++ } } @@ -233,23 +233,24 @@ func (h *connectionsHub) AllowWaitingForTrust(ski string) bool { func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.ShipState) { service := h.serviceForSKI(ski) - if state.State == ship.SmeComplete { - h.EnablePairingForSKI(ski, true) + // overwrite service Paired value + if state.State == ship.SmeHelloStateOk { + h.RegisterRemoteSKI(ski, true) } pairingState := h.mapShipMessageExchangeState(state.State, ski) if state.Error != nil && state.Error != ErrConnectionNotFound { - pairingState = PairingStateError + pairingState = ConnectionStateError } - pairingDetail := PairingDetail{ + pairingDetail := ConnectionStateDetail{ State: pairingState, Error: state.Error, } - existingDetails := service.PairingDetail + existingDetails := service.ConnectionStateDetail if existingDetails.State != pairingState || existingDetails.Error != state.Error { - service.PairingDetail = pairingDetail + service.ConnectionStateDetail = pairingDetail h.serviceProvider.ServicePairingDetailUpdate(ski, pairingDetail) } @@ -261,51 +262,55 @@ func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.S // // ErrNotPaired if the SKI is not in the (to be) paired list // ErrNoConnectionFound if no connection for the SKI was found -func (h *connectionsHub) PairingDetailForSki(ski string) PairingDetail { +func (h *connectionsHub) PairingDetailForSki(ski string) ConnectionStateDetail { service := h.serviceForSKI(ski) if conn := h.connectionForSKI(ski); conn != nil { shipState, shipError := conn.ShipHandshakeState() state := h.mapShipMessageExchangeState(shipState, ski) - return PairingDetail{ + return ConnectionStateDetail{ State: state, Error: shipError, } } - return service.PairingDetail + return service.ConnectionStateDetail } // maps ShipMessageExchangeState to PairingState -func (h *connectionsHub) mapShipMessageExchangeState(state ship.ShipMessageExchangeState, ski string) PairingState { - var connState PairingState +func (h *connectionsHub) mapShipMessageExchangeState(state ship.ShipMessageExchangeState, ski string) ConnectionState { + var connState ConnectionState // map the SHIP states to a public gState switch state { case ship.CmiStateInitStart: - connState = PairingStateQueued + connState = ConnectionStateQueued case ship.CmiStateClientSend, ship.CmiStateClientWait, ship.CmiStateClientEvaluate, ship.CmiStateServerWait, ship.CmiStateServerEvaluate: - connState = PairingStateInitiated + connState = ConnectionStateInitiated case ship.SmeHelloStateReadyInit, ship.SmeHelloStateReadyListen, ship.SmeHelloStateReadyTimeout: - connState = PairingStateInProgress + connState = ConnectionStateInProgress case ship.SmeHelloStatePendingInit, ship.SmeHelloStatePendingListen, ship.SmeHelloStatePendingTimeout: - connState = PairingStateReceived + connState = ConnectionStateReceivedPairingRequest + case ship.SmeHelloStateOk: + connState = ConnectionStateTrusted case ship.SmeHelloStateAbort, ship.SmeHelloStateAbortDone: - connState = PairingStateNone + connState = ConnectionStateNone + case ship.SmeHelloStateRemoteAbortDone: + connState = ConnectionStateRemoteDeniedTrust case ship.SmePinStateCheckInit, ship.SmePinStateCheckListen, ship.SmePinStateCheckError, ship.SmePinStateCheckBusyInit, ship.SmePinStateCheckBusyWait, ship.SmePinStateCheckOk, ship.SmePinStateAskInit, ship.SmePinStateAskProcess, ship.SmePinStateAskRestricted, ship.SmePinStateAskOk: - connState = PairingStatePin - case ship.SmeAccessMethodsRequest, ship.SmeApproved: - connState = PairingStateInProgress - case ship.SmeComplete: - connState = PairingStateAccepted - case ship.SmeError: - connState = PairingStateError + connState = ConnectionStatePin + case ship.SmeAccessMethodsRequest, ship.SmeStateApproved: + connState = ConnectionStateInProgress + case ship.SmeStateComplete: + connState = ConnectionStateCompleted + case ship.SmeStateError: + connState = ConnectionStateError default: - connState = PairingStateInProgress + connState = ConnectionStateInProgress } return connState @@ -456,7 +461,7 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Check if the remote service is paired service := h.serviceForSKI(remoteService.SKI) - if service.PairingDetail.State == PairingStateQueued { + if service.ConnectionStateDetail.State == ConnectionStateQueued { // Check if pairing is made possible if !h.serviceProvider.AllowWaitingForTrust(ski) { logging.Log.Debug("ski", ski, "is not paired, closing the connection") @@ -466,8 +471,8 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - service.PairingDetail.State = PairingStateReceived - h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) + service.ConnectionStateDetail.State = ConnectionStateReceivedPairingRequest + h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) } remoteService = service @@ -609,7 +614,7 @@ func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { service, ok := h.remoteServices[ski] if !ok { service = NewServiceDetails(ski) - service.PairingDetail.State = PairingStateNone + service.ConnectionStateDetail.State = ConnectionStateNone h.remoteServices[ski] = service } @@ -619,9 +624,9 @@ func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { // Sets the SKI as being paired or not // Should be used for services which completed the pairing process and where // stored as having the process completed -func (h *connectionsHub) EnablePairingForSKI(ski string, enable bool) { +func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { service := h.serviceForSKI(ski) - service.Paired = enable + service.Trusted = enable if enable { return @@ -629,10 +634,10 @@ func (h *connectionsHub) EnablePairingForSKI(ski string, enable bool) { h.removeConnectionAttemptCounter(ski) - service.PairingDetail.State = PairingStateNone - service.Paired = false + service.ConnectionStateDetail.State = ConnectionStateNone + service.Trusted = false - h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) + h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) if existingC := h.connectionForSKI(ski); existingC != nil { existingC.CloseConnection(true, "pairing cancelled") @@ -652,9 +657,9 @@ func (h *connectionsHub) InitiatePairingWithSKI(ski string) { // locally initiated service := h.serviceForSKI(ski) - service.PairingDetail.State = PairingStateQueued + service.ConnectionStateDetail.State = ConnectionStateQueued - h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) + h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) // initiate a search and also a connection if it does not yet exist if !h.isSkiConnected(service.SKI) { @@ -671,10 +676,10 @@ func (h *connectionsHub) CancelPairingWithSKI(ski string) { } service := h.serviceForSKI(ski) - service.PairingDetail.State = PairingStateNone - service.Paired = false + service.ConnectionStateDetail.State = ConnectionStateNone + service.Trusted = false - h.serviceProvider.ServicePairingDetailUpdate(ski, service.PairingDetail) + h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) } // Process reported mDNS services @@ -694,8 +699,8 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]MdnsEntry) { // Check if the remote service is paired or queued for connection service := h.serviceForSKI(ski) - pairingState := service.PairingDetail.State - if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != PairingStateQueued { + pairingState := service.ConnectionStateDetail.State + if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != ConnectionStateQueued { continue } @@ -733,7 +738,7 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry MdnsEn counter, duration := h.getConnectionInitiationDelayTime(ski) service := h.serviceForSKI(ski) - if service.PairingDetail.State == PairingStateQueued { + if service.ConnectionStateDetail.State == ConnectionStateQueued { go h.prepareConnectionInitation(ski, counter, entry) return } @@ -766,8 +771,8 @@ func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, ent // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing - pairingState := h.serviceForSKI(ski).PairingDetail.State - if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != PairingStateQueued { + pairingState := h.serviceForSKI(ski).ConnectionStateDetail.State + if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != ConnectionStateQueued { return } @@ -791,8 +796,8 @@ func (h *connectionsHub) initateConnection(remoteService *ServiceDetails, entry for _, address := range entry.Addresses { // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing - pairingState := h.serviceForSKI(remoteService.SKI).PairingDetail.State - if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != PairingStateQueued { + pairingState := h.serviceForSKI(remoteService.SKI).ConnectionStateDetail.State + if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != ConnectionStateQueued { return false } diff --git a/service/hub_test.go b/service/hub_test.go index 75f047ed..9d842641 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -84,14 +84,14 @@ func (s *HubSuite) Test_IsRemoteSKIPaired() { // mark it as connected, so mDNS is not triggered sut.connections[ski] = &ship.ShipConnection{} - sut.EnablePairingForSKI(ski, true) + sut.RegisterRemoteSKI(ski, true) paired = sut.IsRemoteServiceForSKIPaired(ski) assert.Equal(s.T(), true, paired) // remove the connection, so the test doesn't try to close it delete(sut.connections, ski) - sut.EnablePairingForSKI(ski, false) + sut.RegisterRemoteSKI(ski, false) paired = sut.IsRemoteServiceForSKIPaired(ski) assert.Equal(s.T(), false, paired) } diff --git a/service/mock_hub.go b/service/mock_hub.go index a26ea7d1..25ccdb98 100644 --- a/service/mock_hub.go +++ b/service/mock_hub.go @@ -72,7 +72,7 @@ func (mr *MockServiceProviderMockRecorder) RemoteSKIDisconnected(arg0 interface{ } // ServicePairingDetailUpdate mocks base method. -func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 PairingDetail) { +func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 ConnectionStateDetail) { m.ctrl.T.Helper() m.ctrl.Call(m, "ServicePairingDetailUpdate", arg0, arg1) } diff --git a/service/service.go b/service/service.go index d226d09a..2e1ff909 100644 --- a/service/service.go +++ b/service/service.go @@ -38,7 +38,7 @@ type EEBUSServiceHandler interface { // Provides the current pairing state for the remote service // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process - ServicePairingDetailUpdate(ski string, detail PairingDetail) + ServicePairingDetailUpdate(ski string, detail ConnectionStateDetail) // return if the user is still able to trust the connection AllowWaitingForTrust(ski string) bool @@ -109,12 +109,12 @@ func (s *EEBUSService) ServiceShipIDUpdate(ski string, shipdID string) { // Provides the current pairing state for the remote service // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process -func (s *EEBUSService) ServicePairingDetailUpdate(ski string, detail PairingDetail) { +func (s *EEBUSService) ServicePairingDetailUpdate(ski string, detail ConnectionStateDetail) { s.serviceHandler.ServicePairingDetailUpdate(ski, detail) } // Get the current pairing details for a given SKI -func (s *EEBUSService) PairingDetailForSki(ski string) PairingDetail { +func (s *EEBUSService) PairingDetailForSki(ski string) ConnectionStateDetail { return s.connectionsHub.PairingDetailForSki(ski) } @@ -282,8 +282,8 @@ func (s *EEBUSService) RemoteServiceForSKI(ski string) *ServiceDetails { // Adds a new device to the list of known devices which can be connected to // and connect it if it is currently not connected -func (s *EEBUSService) EnablePairingForSKI(ski string, enable bool) { - s.connectionsHub.EnablePairingForSKI(ski, enable) +func (s *EEBUSService) RegisterRemoteSKI(ski string, enable bool) { + s.connectionsHub.RegisterRemoteSKI(ski, enable) } // Triggers the pairing process for a SKI diff --git a/service/types.go b/service/types.go index 97209355..d3340652 100644 --- a/service/types.go +++ b/service/types.go @@ -11,23 +11,25 @@ import ( const defaultPort int = 4711 -// pairing state for global usage, e.g. UI -type PairingState uint +// connection state for global usage, e.g. UI +type ConnectionState uint const ( - PairingStateNone PairingState = iota // The initial state, when no pairing exists - PairingStateQueued // The pairing request has been started and is pending connection initialization - PairingStateInitiated // This service initiated the pairing process - PairingStateReceived // A remote service initiated the pairing process - PairingStateInProgress // The pairing handshake is in progress - PairingStatePin // PIN processing, not supported right now! - PairingStateAccepted // The pairing handshake is accepted from both ends - PairingStateError // The pairing resulted in an error + ConnectionStateNone ConnectionState = iota // The initial state, when no pairing exists + ConnectionStateQueued // The connection request has been started and is pending connection initialization + ConnectionStateInitiated // This service initiated the connection process + ConnectionStateReceivedPairingRequest // A remote service initiated the connection process + ConnectionStateInProgress // The connection handshake is in progress + ConnectionStateTrusted // The connection is trusted on both ends + ConnectionStatePin // PIN processing, not supported right now! + ConnectionStateCompleted // The connection handshake is completed from both ends + ConnectionStateRemoteDeniedTrust // The remote service denied trust + ConnectionStateError // The connection handshake resulted in an error ) -// the pairing state of a service and error if applicable -type PairingDetail struct { - State PairingState +// the connection state of a service and error if applicable +type ConnectionStateDetail struct { + State ConnectionState Error error } @@ -54,12 +56,13 @@ type ServiceDetails struct { // Flags if the service auto auto accepts other services RegisterAutoAccept bool - // Flags if the service is paired and should be connected to - // Should be enabled after the pairing process resulted PairingDetail = PairingStateAccepted - Paired bool + // Flags if the service is trusted and should be reconnected to + // Should be enabled after the connection process resulted + // ConnectionStateDetail == ConnectionStateTrusted the first time + Trusted bool - // the current pairing details - PairingDetail PairingDetail + // the current connection state details + ConnectionStateDetail ConnectionStateDetail } // create a new ServiceDetails record with a SKI diff --git a/ship/connection.go b/ship/connection.go index f1f756eb..c66637b3 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -146,7 +146,7 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { c.removeRemoteDeviceConnection() // this may not be used for Connection Data Exchange is entered! - if safe && c.getState() == SmeComplete { + if safe && c.getState() == SmeStateComplete { // SHIP 13.4.7: Connection Termination Announce closeMessage := model.ConnectionClose{ ConnectionClose: model.ConnectionCloseType{ @@ -157,7 +157,7 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { _ = c.sendShipModel(model.MsgTypeEnd, closeMessage) - if c.getState() != SmeError { + if c.getState() != SmeStateError { return } } @@ -166,7 +166,7 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { // handshake is completed if approved or aborted state := c.getState() - handshakeEnd := state == SmeComplete || state == SmeHelloStateAbort || state == SmeHelloStateAbortDone + handshakeEnd := state == SmeStateComplete || state == SmeHelloStateAbortDone || state == SmeHelloStateRemoteAbortDone c.serviceDataProvider.HandleConnectionClosed(c, handshakeEnd) }) @@ -233,16 +233,23 @@ func (c *ShipConnection) hasSpineDatagram(message []byte) bool { func (c *ShipConnection) ReportConnectionError(err error) { // if the handshake is aborted, a closed connection is no error currentState := c.getState() - if currentState == SmeHelloStateAbort || currentState == SmeHelloStateAbortDone { + if currentState == SmeHelloStateRemoteAbortDone { + // remote service should close the connection return } - c.setState(SmeError, err) + if currentState == SmeHelloStateAbort || + currentState == SmeHelloStateAbortDone { + c.CloseConnection(false, "") + return + } + + c.setState(SmeStateError, err) c.CloseConnection(false, "") state := ShipState{ - State: SmeError, + State: SmeStateError, Error: err, } c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.RemoteSKI, state) diff --git a/ship/handshake.go b/ship/handshake.go index 0fa50202..159e6ff2 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -30,11 +30,11 @@ func (c *ShipConnection) handleShipMessage(timeout bool, message []byte) { // c.DataHandler.CloseDataConnection(4001, "close") - c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeComplete) + c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeStateComplete) case model.ConnectionClosePhaseTypeConfirm: // we got a confirmation so close this connection c.DataHandler.CloseDataConnection(4001, "close") - c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeComplete) + c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeStateComplete) } return @@ -59,7 +59,7 @@ func (c *ShipConnection) setState(newState ShipMessageExchangeState, err error) c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) case SmeHelloStateOk: c.stopHandshakeTimer() - case SmeHelloStateAbort, SmeHelloStateAbortDone: + case SmeHelloStateAbort, SmeHelloStateAbortDone, SmeHelloStateRemoteAbortDone: c.stopHandshakeTimer() case SmeProtHStateClientListenChoice: c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) @@ -91,7 +91,7 @@ func (c *ShipConnection) getState() ShipMessageExchangeState { // handle handshake state transitions func (c *ShipConnection) handleState(timeout bool, message []byte) { switch c.getState() { - case SmeError: + case SmeStateError: logging.Log.Debug(c.RemoteSKI, "connection is in error state") return @@ -166,7 +166,7 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { case SmeHelloStateAbort: c.handshakeHello_Abort() - case SmeHelloStateAbortDone: + case SmeHelloStateAbortDone, SmeHelloStateRemoteAbortDone: go func() { time.Sleep(time.Second) c.CloseConnection(false, "") @@ -215,18 +215,24 @@ func (c *ShipConnection) approveHandshake() { // Report to SPINE local device about this remote device connection c.spineDataProcessing = c.deviceLocalCon.AddRemoteDevice(c.RemoteSKI, c) c.stopHandshakeTimer() - c.setState(SmeComplete, nil) + c.setState(SmeStateComplete, nil) } // end the handshake process because of an error func (c *ShipConnection) endHandshakeWithError(err error) { c.stopHandshakeTimer() - c.setState(SmeError, err) + c.setState(SmeStateError, err) logging.Log.Debug(c.RemoteSKI, "SHIP handshake error:", err) c.CloseConnection(true, err.Error()) + + state := ShipState{ + State: SmeStateError, + Error: err, + } + c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.RemoteSKI, state) } // set the handshake timer to a new duration and start the channel diff --git a/ship/hs_access.go b/ship/hs_access.go index 8be73f88..81155a28 100644 --- a/ship/hs_access.go +++ b/ship/hs_access.go @@ -76,6 +76,6 @@ func (c *ShipConnection) handshakeAccessMethods_Request(message []byte) { return } - c.setState(SmeApproved, nil) + c.setState(SmeStateApproved, nil) c.approveHandshake() } diff --git a/ship/hs_access_test.go b/ship/hs_access_test.go index 7223bc17..605d83b8 100644 --- a/ship/hs_access_test.go +++ b/ship/hs_access_test.go @@ -68,7 +68,7 @@ func (s *AccessSuite) Test_Methods_Ok() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeComplete, sut.getState()) + assert.Equal(s.T(), SmeStateComplete, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -91,7 +91,7 @@ func (s *AccessSuite) Test_Methods_WrongShipID() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_hello.go b/ship/hs_hello.go index 38efa5c2..f7f70005 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -60,7 +60,7 @@ func (c *ShipConnection) handshakeHello_ReadyListen(message []byte) { // TODO: what to do if this is false? case model.ConnectionHelloPhaseTypeAborted: - c.setState(SmeHelloStateAbortDone, nil) + c.setState(SmeHelloStateRemoteAbortDone, nil) c.handleState(false, nil) return @@ -181,7 +181,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { c.handleState(false, nil) case model.ConnectionHelloPhaseTypeAborted: - c.setState(SmeHelloStateAbortDone, nil) + c.setState(SmeHelloStateRemoteAbortDone, nil) c.handleState(false, nil) return diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go index 5c798769..b95e08ad 100644 --- a/ship/hs_hello_test.go +++ b/ship/hs_hello_test.go @@ -127,7 +127,7 @@ func (s *HelloSuite) Test_ReadyListen_Abort() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) + assert.Equal(s.T(), SmeHelloStateRemoteAbortDone, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -241,7 +241,7 @@ func (s *HelloSuite) Test_PendingListen_Abort() { sut.handleShipMessage(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) + assert.Equal(s.T(), SmeHelloStateRemoteAbortDone, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_init_client_test.go b/ship/hs_init_client_test.go index 7db506e5..2e095b7a 100644 --- a/ship/hs_init_client_test.go +++ b/ship/hs_init_client_test.go @@ -65,7 +65,7 @@ func (s *InitClientSuite) Test_ClientWait_Timeout() { sut.handleState(true, nil) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) assert.Equal(s.T(), data.handleConnectionClosedInvoked, true) @@ -79,7 +79,7 @@ func (s *InitClientSuite) Test_ClientWait_InvalidMsgType() { sut.handleState(false, []byte{0x05, 0x00}) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -92,7 +92,7 @@ func (s *InitClientSuite) Test_ClientWait_InvalidData() { sut.handleState(false, []byte{model.MsgTypeInit, 0x05}) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_init_server_test.go b/ship/hs_init_server_test.go index fdf0e573..a7125455 100644 --- a/ship/hs_init_server_test.go +++ b/ship/hs_init_server_test.go @@ -63,7 +63,7 @@ func (s *InitServerSuite) Test_ServerWait_InvalidMsgType() { sut.handleState(false, []byte{0x05, 0x00}) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -76,7 +76,7 @@ func (s *InitServerSuite) Test_ServerWait_InvalidData() { sut.handleState(false, []byte{model.MsgTypeInit, 0x05}) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/hs_pin_test.go b/ship/hs_pin_test.go index 90f2be32..5098e6b8 100644 --- a/ship/hs_pin_test.go +++ b/ship/hs_pin_test.go @@ -69,7 +69,7 @@ func (s *PinSuite) Test_CheckListen_Required() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -92,7 +92,7 @@ func (s *PinSuite) Test_CheckListen_Optional() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) @@ -115,7 +115,7 @@ func (s *PinSuite) Test_CheckListen_Ok() { sut.handleState(false, msg) assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeError, sut.getState()) + assert.Equal(s.T(), SmeStateError, sut.getState()) assert.Nil(s.T(), data.lastMessage()) shutdownTest(sut) diff --git a/ship/types.go b/ship/types.go index b51bb9e0..48e61286 100644 --- a/ship/types.go +++ b/ship/types.go @@ -70,8 +70,9 @@ const ( SmeHelloStatePendingListen SmeHelloStatePendingTimeout SmeHelloStateOk - SmeHelloStateAbort - SmeHelloStateAbortDone + SmeHelloStateAbort // Sent abort to remote + SmeHelloStateAbortDone // Sending abort to remote is done + SmeHelloStateRemoteAbortDone // Received abort from remote // Connection State Protocol Handhsake SHIP 13.4.4.2 SmeProtHStateServerInit @@ -97,13 +98,13 @@ const ( SmeAccessMethodsRequest // Handshake approved on both ends - SmeApproved + SmeStateApproved // Handshake process is successfully completed - SmeComplete + SmeStateComplete // Handshake ended with an error - SmeError + SmeStateError ) var shipInit []byte = []byte{model.MsgTypeInit, 0x00} diff --git a/ship/websocket.go b/ship/websocket.go index 75ffa5a3..9b2bd8b4 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -110,6 +110,11 @@ func (w *websocketConnection) writeShipPump() { } if err := w.writeMessage(websocket.BinaryMessage, message); err != nil { + // ignore write errors if the connection got closed + if w.isConnClosed() { + return + } + logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) w.setConnClosedError(err) w.dataProcessing.ReportConnectionError(err) @@ -153,6 +158,11 @@ func (w *websocketConnection) readShipPump() { } message, err := w.readWebsocketMessage() + // ignore read errors if the connection got closed + if w.isConnClosed() { + return + } + if err != nil { logging.Log.Debug(w.remoteSki, "websocket read error: ", err) w.close() From 849d9abecc2e0d003bb4b0285b0bca794646f3ba Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 18:27:42 +0200 Subject: [PATCH 016/240] Ignore websocket close errors for real Controlled closes should not result in error messages --- ship/websocket.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ship/websocket.go b/ship/websocket.go index 9b2bd8b4..2bb1d092 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -275,6 +275,7 @@ func (w *websocketConnection) CloseDataConnection(closeCode int, reason string) if reason != "" { _ = w.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, reason)) } + w.setConnClosedError(nil) w.close() } } From 594739df61ad1b61c80ba99e4e92aff4d5eeca70 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 19:07:33 +0200 Subject: [PATCH 017/240] Add support for denying trust via disconnect --- service/hub.go | 2 +- ship/connection.go | 13 ++++++++++++- ship/handshake.go | 2 +- ship/types.go | 1 + 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/service/hub.go b/service/hub.go index e2571c76..e818a0c9 100644 --- a/service/hub.go +++ b/service/hub.go @@ -296,7 +296,7 @@ func (h *connectionsHub) mapShipMessageExchangeState(state ship.ShipMessageExcha connState = ConnectionStateTrusted case ship.SmeHelloStateAbort, ship.SmeHelloStateAbortDone: connState = ConnectionStateNone - case ship.SmeHelloStateRemoteAbortDone: + case ship.SmeHelloStateRemoteAbortDone, ship.SmeHelloStateRejected: connState = ConnectionStateRemoteDeniedTrust case ship.SmePinStateCheckInit, ship.SmePinStateCheckListen, ship.SmePinStateCheckError, ship.SmePinStateCheckBusyInit, ship.SmePinStateCheckBusyWait, ship.SmePinStateCheckOk, diff --git a/ship/connection.go b/ship/connection.go index c66637b3..ad42fb67 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -166,7 +166,10 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { // handshake is completed if approved or aborted state := c.getState() - handshakeEnd := state == SmeStateComplete || state == SmeHelloStateAbortDone || state == SmeHelloStateRemoteAbortDone + handshakeEnd := state == SmeStateComplete || + state == SmeHelloStateAbortDone || + state == SmeHelloStateRemoteAbortDone || + state == SmeHelloStateRejected c.serviceDataProvider.HandleConnectionClosed(c, handshakeEnd) }) @@ -233,6 +236,14 @@ func (c *ShipConnection) hasSpineDatagram(message []byte) bool { func (c *ShipConnection) ReportConnectionError(err error) { // if the handshake is aborted, a closed connection is no error currentState := c.getState() + + // rejections are also received by sending `{"connectionHello":[{"phase":"pending"},{"waiting":60000}]}` + // and then closing the websocket connection with `4452: Node rejected by application.` + if currentState == SmeHelloStateReadyListen { + c.setState(SmeHelloStateRejected, nil) + return + } + if currentState == SmeHelloStateRemoteAbortDone { // remote service should close the connection return diff --git a/ship/handshake.go b/ship/handshake.go index 159e6ff2..3fd9a517 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -59,7 +59,7 @@ func (c *ShipConnection) setState(newState ShipMessageExchangeState, err error) c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) case SmeHelloStateOk: c.stopHandshakeTimer() - case SmeHelloStateAbort, SmeHelloStateAbortDone, SmeHelloStateRemoteAbortDone: + case SmeHelloStateAbort, SmeHelloStateAbortDone, SmeHelloStateRemoteAbortDone, SmeHelloStateRejected: c.stopHandshakeTimer() case SmeProtHStateClientListenChoice: c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) diff --git a/ship/types.go b/ship/types.go index 48e61286..47d62c52 100644 --- a/ship/types.go +++ b/ship/types.go @@ -73,6 +73,7 @@ const ( SmeHelloStateAbort // Sent abort to remote SmeHelloStateAbortDone // Sending abort to remote is done SmeHelloStateRemoteAbortDone // Received abort from remote + SmeHelloStateRejected // Connection closed after remote pending: "4452: Node rejected by application" // Connection State Protocol Handhsake SHIP 13.4.4.2 SmeProtHStateServerInit From d3708b5871ee8d86c134c676360930851823a2e3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 19:10:32 +0200 Subject: [PATCH 018/240] End HelloAbort the with the same error --- ship/handshake.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ship/handshake.go b/ship/handshake.go index 3fd9a517..05781a44 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -169,7 +169,7 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { case SmeHelloStateAbortDone, SmeHelloStateRemoteAbortDone: go func() { time.Sleep(time.Second) - c.CloseConnection(false, "") + c.CloseConnection(false, "4452: Node rejected by application") }() // smeProtocol From 9dc6d795b2208ef5d43f69a66513f084269a9358 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 22:21:58 +0200 Subject: [PATCH 019/240] Always allow connections for registered services --- service/hub.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/service/hub.go b/service/hub.go index e818a0c9..52b675c3 100644 --- a/service/hub.go +++ b/service/hub.go @@ -226,6 +226,12 @@ func (h *connectionsHub) ReportServiceShipID(ski string, shipdID string) { // return if the user is still able to trust the connection func (h *connectionsHub) AllowWaitingForTrust(ski string) bool { + if service := h.serviceForSKI(ski); service != nil { + if service.Trusted { + return true + } + } + return h.serviceProvider.AllowWaitingForTrust(ski) } @@ -462,15 +468,6 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Check if the remote service is paired service := h.serviceForSKI(remoteService.SKI) if service.ConnectionStateDetail.State == ConnectionStateQueued { - // Check if pairing is made possible - if !h.serviceProvider.AllowWaitingForTrust(ski) { - logging.Log.Debug("ski", ski, "is not paired, closing the connection") - msg := "Node rejected by application" - _ = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(4452, msg)) - _ = conn.Close() - return - } - service.ConnectionStateDetail.State = ConnectionStateReceivedPairingRequest h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) } From 79f79339d79779080f7776e31629da8d34368eea Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 22:22:25 +0200 Subject: [PATCH 020/240] Reject right away if trust is not possible --- ship/hs_hello.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ship/hs_hello.go b/ship/hs_hello.go index f7f70005..33ab56ac 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -97,6 +97,11 @@ func (c *ShipConnection) handshakeHello_PendingInit() { } c.setState(SmeHelloStatePendingListen, nil) + + if !c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { + c.setState(SmeHelloStateAbort, nil) + c.handleState(false, nil) + } } // SME_HELLO_PENDING_LISTEN From 1604907fa6c2de4fefd218733baabbdeb494bd00 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 22:22:46 +0200 Subject: [PATCH 021/240] Add option to provide close code --- service/hub.go | 8 ++++---- ship/connection.go | 16 ++++++++++------ ship/handshake.go | 4 ++-- ship/hs_prot.go | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/service/hub.go b/service/hub.go index 52b675c3..753a77a0 100644 --- a/service/hub.go +++ b/service/hub.go @@ -334,7 +334,7 @@ func (h *connectionsHub) DisconnectSKI(ski string, reason string) { return } - con.CloseConnection(true, reason) + con.CloseConnection(true, 0, reason) } // register a new ship Connection @@ -366,7 +366,7 @@ func (h *connectionsHub) connectionForSKI(ski string) *ship.ShipConnection { func (h *connectionsHub) shutdown() { h.mdns.ShutdownMdnsService() for _, c := range h.connections { - c.CloseConnection(false, "") + c.CloseConnection(false, 0, "") } } @@ -586,7 +586,7 @@ func (h *connectionsHub) keepThisConnection(conn *websocket.Conn, incomingReques // we have an existing connection // so keep the new (most recent) and close the old one logging.Log.Debug("closing existing double connection") - go existingC.CloseConnection(false, "") + go existingC.CloseConnection(false, 0, "") } else { connType := "incoming" if !incomingRequest { @@ -637,7 +637,7 @@ func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) if existingC := h.connectionForSKI(ski); existingC != nil { - existingC.CloseConnection(true, "pairing cancelled") + existingC.CloseConnection(true, 0, "pairing cancelled") } } diff --git a/ship/connection.go b/ship/connection.go index ad42fb67..96e88bc6 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -139,7 +139,7 @@ func (c *ShipConnection) removeRemoteDeviceConnection() { } // close this ship connection -func (c *ShipConnection) CloseConnection(safe bool, reason string) { +func (c *ShipConnection) CloseConnection(safe bool, code int, reason string) { c.shutdownOnce.Do(func() { c.stopHandshakeTimer() @@ -162,7 +162,11 @@ func (c *ShipConnection) CloseConnection(safe bool, reason string) { } } - c.DataHandler.CloseDataConnection(4001, reason) + closeCode := 4001 + if code != 0 { + closeCode = code + } + c.DataHandler.CloseDataConnection(closeCode, reason) // handshake is completed if approved or aborted state := c.getState() @@ -251,13 +255,13 @@ func (c *ShipConnection) ReportConnectionError(err error) { if currentState == SmeHelloStateAbort || currentState == SmeHelloStateAbortDone { - c.CloseConnection(false, "") + c.CloseConnection(false, 4452, "Node rejected by application") return } c.setState(SmeStateError, err) - c.CloseConnection(false, "") + c.CloseConnection(false, 0, "") state := ShipState{ State: SmeStateError, @@ -313,7 +317,7 @@ func (c *ShipConnection) sendSpineData(data []byte) error { } if isClosed, err := c.DataHandler.IsDataConnectionClosed(); isClosed { - c.CloseConnection(false, "") + c.CloseConnection(false, 0, "") return err } @@ -355,7 +359,7 @@ func (c *ShipConnection) processShipJsonMessage(message []byte, target any) erro // transform a SHIP model into EEBUS specific JSON func (c *ShipConnection) shipMessage(typ byte, model interface{}) ([]byte, error) { if isClosed, err := c.DataHandler.IsDataConnectionClosed(); isClosed { - c.CloseConnection(false, "") + c.CloseConnection(false, 0, "") return nil, err } diff --git a/ship/handshake.go b/ship/handshake.go index 05781a44..1452ae04 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -169,7 +169,7 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { case SmeHelloStateAbortDone, SmeHelloStateRemoteAbortDone: go func() { time.Sleep(time.Second) - c.CloseConnection(false, "4452: Node rejected by application") + c.CloseConnection(false, 4452, "Node rejected by application") }() // smeProtocol @@ -226,7 +226,7 @@ func (c *ShipConnection) endHandshakeWithError(err error) { logging.Log.Debug(c.RemoteSKI, "SHIP handshake error:", err) - c.CloseConnection(true, err.Error()) + c.CloseConnection(true, 0, err.Error()) state := ShipState{ State: SmeStateError, diff --git a/ship/hs_prot.go b/ship/hs_prot.go index 3775f1bd..bcacf6f7 100644 --- a/ship/hs_prot.go +++ b/ship/hs_prot.go @@ -171,5 +171,5 @@ func (c *ShipConnection) abortProtocolHandshake(err model.MessageProtocolHandsha _ = c.sendShipModel(model.MsgTypeControl, msg) - c.CloseConnection(false, "") + c.CloseConnection(false, 0, "") } From 4c7f8ebac04947fe11992806b1f856e549556ac0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 22:29:34 +0200 Subject: [PATCH 022/240] Fix race condition --- ship/handshake.go | 16 +++++++++++++++- ship/hs_hello.go | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ship/handshake.go b/ship/handshake.go index 1452ae04..3df62c64 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -240,7 +240,7 @@ func (c *ShipConnection) setHandshakeTimer(timerType timeoutTimerType, duration c.stopHandshakeTimer() c.setHandshakeTimerRunning(true) - c.handshakeTimerType = timerType + c.setHandshakeTimerType(timerType) go func() { select { @@ -280,3 +280,17 @@ func (c *ShipConnection) getHandshakeTimerRunnging() bool { return c.handshakeTimerRunning } + +func (c *ShipConnection) setHandshakeTimerType(timerType timeoutTimerType) { + c.handshakeTimerMux.Lock() + defer c.handshakeTimerMux.Unlock() + + c.handshakeTimerType = timerType +} + +func (c *ShipConnection) getHandshakeTimerType() timeoutTimerType { + c.handshakeTimerMux.Lock() + defer c.handshakeTimerMux.Unlock() + + return c.handshakeTimerType +} diff --git a/ship/hs_hello.go b/ship/hs_hello.go index 33ab56ac..eb9cb57b 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -212,7 +212,7 @@ func (c *ShipConnection) handshakeHello_PendingProlongationRequest() { } func (c *ShipConnection) handshakeHello_PendingTimeout() { - if c.handshakeTimerType != timeoutTimerTypeSendProlongationRequest { + if c.getHandshakeTimerType() != timeoutTimerTypeSendProlongationRequest { c.setState(SmeHelloStateAbort, nil) c.handleState(false, nil) return From aec8312b2637d31d0d20d3b791a3504323d3e8ce Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 22:30:58 +0200 Subject: [PATCH 023/240] Fix test --- ship/hs_hello_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go index b95e08ad..4c5cff75 100644 --- a/ship/hs_hello_test.go +++ b/ship/hs_hello_test.go @@ -139,8 +139,8 @@ func (s *HelloSuite) Test_PendingInit() { sut.setState(SmeHelloStatePendingInit, nil) sut.handleState(false, nil) - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) + assert.Equal(s.T(), false, sut.handshakeTimerRunning) + assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) From 90ffa1333f30deee01becb76cc37855ec274dcda Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 29 May 2023 22:46:18 +0200 Subject: [PATCH 024/240] Close the connection on rejections and abort --- ship/connection.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ship/connection.go b/ship/connection.go index 96e88bc6..c4a11b70 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -245,11 +245,13 @@ func (c *ShipConnection) ReportConnectionError(err error) { // and then closing the websocket connection with `4452: Node rejected by application.` if currentState == SmeHelloStateReadyListen { c.setState(SmeHelloStateRejected, nil) + c.CloseConnection(false, 0, "") return } if currentState == SmeHelloStateRemoteAbortDone { // remote service should close the connection + c.CloseConnection(false, 0, "") return } From dfdc047760e9068b44f9df7953d956f2c9604a3b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 30 May 2023 15:08:51 +0200 Subject: [PATCH 025/240] Update websocket close code and message on unpair --- service/hub.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/hub.go b/service/hub.go index 753a77a0..b0467b4a 100644 --- a/service/hub.go +++ b/service/hub.go @@ -637,7 +637,7 @@ func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) if existingC := h.connectionForSKI(ski); existingC != nil { - existingC.CloseConnection(true, 0, "pairing cancelled") + existingC.CloseConnection(true, 4500, "User close") } } From 93ff2e34717b15b67e154bb61071bd74730d095f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 2 Jun 2023 19:25:04 +0200 Subject: [PATCH 026/240] Usecase improvements - Send an event when UseCase data was received - Clean up all known usecases when new data was received - Add detail about UseCase availability - Fix actor being overwritten when having multiple on the same device --- spine/nodemanagement_usecase.go | 22 ++++++++++++++++++++++ spine/usecase.go | 8 ++++---- spine/usecase_manager.go | 17 ++++++++++++----- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go index f5907b5f..6ff6fbdc 100644 --- a/spine/nodemanagement_usecase.go +++ b/spine/nodemanagement_usecase.go @@ -6,6 +6,7 @@ import ( "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" ) func (r *NodeManagementImpl) RequestUseCaseData(remoteDeviceSki string, remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *ErrorType) { @@ -34,6 +35,8 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data mode } remoteUseCaseManager := message.FeatureRemote.Device().UseCaseManager() + remoteUseCaseManager.RemoveAll() + for _, useCaseInfo := range useCaseInformation { // this is mandatory var actor model.UseCaseActorType @@ -56,6 +59,11 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data mode } // this is optional + useCaseAvailable := true + if useCaseSupport.UseCaseAvailable != nil { + useCaseAvailable = *useCaseSupport.UseCaseAvailable + } + var useCaseVersion model.SpecificationVersionType if useCaseSupport.UseCaseVersion != nil { useCaseVersion = model.SpecificationVersionType(*useCaseSupport.UseCaseVersion) @@ -70,10 +78,24 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data mode actor, useCaseName, useCaseVersion, + useCaseAvailable, useCaseSupport.ScenarioSupport) } } + // the data was updated, so send an event, other event handlers may watch out for this as well + payload := EventPayload{ + Ski: message.FeatureRemote.Device().ski, + EventType: EventTypeDataChange, + ChangeType: ElementChangeUpdate, + Feature: message.FeatureRemote, + Device: message.FeatureRemote.Device(), + Entity: message.FeatureRemote.Entity(), + CmdClassifier: util.Ptr(message.CmdClassifier), + Data: data, + } + Events.Publish(payload) + return nil } diff --git a/spine/usecase.go b/spine/usecase.go index b99e0885..23bcafa1 100644 --- a/spine/usecase.go +++ b/spine/usecase.go @@ -41,17 +41,17 @@ type UseCaseImpl struct { scenarioSupport []model.UseCaseScenarioSupportType } -func NewUseCase(entity *EntityLocalImpl, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { +func NewUseCase(entity *EntityLocalImpl, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { actor := entityTypeActorMap[entity.EntityType()] - return NewUseCaseWithActor(entity, actor, ucEnumType, useCaseVersion, scenarioSupport) + return NewUseCaseWithActor(entity, actor, ucEnumType, useCaseVersion, useCaseAvailable, scenarioSupport) } -func NewUseCaseWithActor(entity *EntityLocalImpl, actor model.UseCaseActorType, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { +func NewUseCaseWithActor(entity *EntityLocalImpl, actor model.UseCaseActorType, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { checkArguments(*entity.EntityImpl, ucEnumType) ucManager := entity.Device().UseCaseManager() - ucManager.Add(actor, ucEnumType, useCaseVersion, scenarioSupport) + ucManager.Add(actor, ucEnumType, useCaseVersion, useCaseAvailable, scenarioSupport) return &UseCaseImpl{ Entity: entity, diff --git a/spine/usecase_manager.go b/spine/usecase_manager.go index 8926fcac..bd5fc67d 100644 --- a/spine/usecase_manager.go +++ b/spine/usecase_manager.go @@ -12,11 +12,12 @@ func NewUseCaseManager() *UseCaseManager { } } -func (r *UseCaseManager) Add(actor model.UseCaseActorType, useCaseName model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, scenarios []model.UseCaseScenarioSupportType) { +func (r *UseCaseManager) Add(actor model.UseCaseActorType, useCaseName model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarios []model.UseCaseScenarioSupportType) { useCaseSupport := model.UseCaseSupportType{ - UseCaseVersion: &useCaseVersion, - UseCaseName: &useCaseName, - ScenarioSupport: scenarios, + UseCaseVersion: &useCaseVersion, + UseCaseName: &useCaseName, + UseCaseAvailable: &useCaseAvailable, + ScenarioSupport: scenarios, } useCaseInfo, exists := r.useCaseInformationMap[actor] @@ -28,13 +29,19 @@ func (r *UseCaseManager) Add(actor model.UseCaseActorType, useCaseName model.Use r.useCaseInformationMap[actor] = useCaseInfo } +// this needs to be called when a new notification or reply will provide a new set of UseCases +func (r *UseCaseManager) RemoveAll() { + r.useCaseInformationMap = make(map[model.UseCaseActorType][]model.UseCaseSupportType) +} + func (r *UseCaseManager) UseCaseInformation() []model.UseCaseInformationDataType { var result []model.UseCaseInformationDataType for actor, useCaseSupport := range r.useCaseInformationMap { + thisActor := actor useCaseInfo := model.UseCaseInformationDataType{ //Address: r.address, // TODO: which address ??? - Actor: &actor, + Actor: &thisActor, UseCaseSupport: useCaseSupport, } result = append(result, useCaseInfo) From a06d01adb2f32560b7605c4aaf729430bfa6d712 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 4 Jun 2023 16:59:57 +0200 Subject: [PATCH 027/240] Add support for updating UC availability This will trigger a notification to all registered remote devices --- spine/device_local.go | 8 ++++++++ spine/nodemanagement_usecase.go | 15 +++++++++++++++ spine/usecase.go | 30 ++++++++++++++++++++++-------- spine/usecase_manager.go | 5 +++++ 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/spine/device_local.go b/spine/device_local.go index fc666fba..6c26e3ba 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -311,6 +311,14 @@ func (r *DeviceLocalImpl) Information() *model.NodeManagementDetailedDiscoveryDe return &res } +// send a notify message to all remote devices +func (r *DeviceLocalImpl) NotifyUseCaseData() { + for _, remoteDevice := range r.remoteDevices { + // TODO: add error management + _, _ = r.nodeManagement.NotifyUseCaseData(remoteDevice) + } +} + func (r *DeviceLocalImpl) NotifySubscribers(featureAddress *model.FeatureAddressType, cmd model.CmdType) { subscriptions := r.SubscriptionManager().SubscriptionsOnFeature(*featureAddress) for _, subscription := range subscriptions { diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go index 6ff6fbdc..c6aae0c5 100644 --- a/spine/nodemanagement_usecase.go +++ b/spine/nodemanagement_usecase.go @@ -17,6 +17,21 @@ func (r *NodeManagementImpl) RequestUseCaseData(remoteDeviceSki string, remoteDe return r.RequestDataBySenderAddress(cmd, sender, remoteDeviceSki, rfAdress, defaultMaxResponseDelay) } +func (r *NodeManagementImpl) NotifyUseCaseData(remoteDevice *DeviceRemoteImpl) (*model.MsgCounterType, error) { + rfAdress := featureAddressType(NodeManagementFeatureId, EntityAddressType(remoteDevice.address, DeviceInformationAddressEntity)) + rEntity := remoteDevice.Entity([]model.AddressEntityType{model.AddressEntityType(DeviceInformationEntityId)}) + + featureRemote := remoteDevice.FeatureByEntityTypeAndRole(rEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + + cmd := model.CmdType{ + NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ + UseCaseInformation: r.entity.Device().UseCaseManager().UseCaseInformation(), + }, + } + + return featureRemote.Sender().Notify(r.Address(), rfAdress, cmd) +} + func (r *NodeManagementImpl) processReadUseCaseData(featureRemote *FeatureRemoteImpl, requestHeader *model.HeaderType) error { cmd := model.CmdType{ diff --git a/spine/usecase.go b/spine/usecase.go index 23bcafa1..f3892f70 100644 --- a/spine/usecase.go +++ b/spine/usecase.go @@ -32,21 +32,26 @@ var useCaseValidActorsMap = map[model.UseCaseNameType][]model.UseCaseActorType{ model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData: {model.UseCaseActorTypeCEM, model.UseCaseActorTypePVSystem, model.UseCaseActorTypeVisualizationAppliance}, } +// defines a specific usecase implementation +// right now this is just used as a wrapper for supported usecases type UseCaseImpl struct { Entity *EntityLocalImpl Actor model.UseCaseActorType - name model.UseCaseNameType - useCaseVersion model.SpecificationVersionType - scenarioSupport []model.UseCaseScenarioSupportType + name model.UseCaseNameType + useCaseVersion model.SpecificationVersionType + useCaseAvailable bool + scenarioSupport []model.UseCaseScenarioSupportType } +// returns a UseCaseImpl with a default mapping of entity to actor func NewUseCase(entity *EntityLocalImpl, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { actor := entityTypeActorMap[entity.EntityType()] return NewUseCaseWithActor(entity, actor, ucEnumType, useCaseVersion, useCaseAvailable, scenarioSupport) } +// returns a UseCaseImpl with specific entity and actor func NewUseCaseWithActor(entity *EntityLocalImpl, actor model.UseCaseActorType, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { checkArguments(*entity.EntityImpl, ucEnumType) @@ -54,11 +59,12 @@ func NewUseCaseWithActor(entity *EntityLocalImpl, actor model.UseCaseActorType, ucManager.Add(actor, ucEnumType, useCaseVersion, useCaseAvailable, scenarioSupport) return &UseCaseImpl{ - Entity: entity, - Actor: actor, - name: model.UseCaseNameType(ucEnumType), - useCaseVersion: useCaseVersion, - scenarioSupport: scenarioSupport, + Entity: entity, + Actor: actor, + name: model.UseCaseNameType(ucEnumType), + useCaseVersion: useCaseVersion, + useCaseAvailable: useCaseAvailable, + scenarioSupport: scenarioSupport, } } @@ -73,6 +79,14 @@ func checkArguments(entity EntityImpl, ucEnumType model.UseCaseNameType) { } } +// Update the availability of this usecase and +// trigger a notification being sent to the remote device +func (u *UseCaseImpl) SetUseCaseAvailable(available bool) { + u.useCaseAvailable = available + + u.Entity.Device().NotifyUseCaseData() +} + /* // This is not yet used, might be removed? func waitForRequest[T any](c chan T, maxDelay time.Duration) *T { diff --git a/spine/usecase_manager.go b/spine/usecase_manager.go index bd5fc67d..ef460687 100644 --- a/spine/usecase_manager.go +++ b/spine/usecase_manager.go @@ -2,16 +2,20 @@ package spine import "github.com/enbility/eebus-go/spine/model" +// manages the supported usecases for a device +// each device has its own UseCaseManager type UseCaseManager struct { useCaseInformationMap map[model.UseCaseActorType][]model.UseCaseSupportType } +// return a new UseCaseManager func NewUseCaseManager() *UseCaseManager { return &UseCaseManager{ useCaseInformationMap: make(map[model.UseCaseActorType][]model.UseCaseSupportType), } } +// add a usecase func (r *UseCaseManager) Add(actor model.UseCaseActorType, useCaseName model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarios []model.UseCaseScenarioSupportType) { useCaseSupport := model.UseCaseSupportType{ UseCaseVersion: &useCaseVersion, @@ -34,6 +38,7 @@ func (r *UseCaseManager) RemoveAll() { r.useCaseInformationMap = make(map[model.UseCaseActorType][]model.UseCaseSupportType) } +// return all actors and their supported usecases func (r *UseCaseManager) UseCaseInformation() []model.UseCaseInformationDataType { var result []model.UseCaseInformationDataType From 44bdd36654139263dcf4c6365174bed527341905 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 4 Jun 2023 19:05:12 +0200 Subject: [PATCH 028/240] Improve usecase actor and entity check --- spine/usecase.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spine/usecase.go b/spine/usecase.go index f3892f70..09e81ecc 100644 --- a/spine/usecase.go +++ b/spine/usecase.go @@ -46,6 +46,8 @@ type UseCaseImpl struct { // returns a UseCaseImpl with a default mapping of entity to actor func NewUseCase(entity *EntityLocalImpl, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { + checkEntityArguments(*entity.EntityImpl) + actor := entityTypeActorMap[entity.EntityType()] return NewUseCaseWithActor(entity, actor, ucEnumType, useCaseVersion, useCaseAvailable, scenarioSupport) @@ -53,7 +55,7 @@ func NewUseCase(entity *EntityLocalImpl, ucEnumType model.UseCaseNameType, useCa // returns a UseCaseImpl with specific entity and actor func NewUseCaseWithActor(entity *EntityLocalImpl, actor model.UseCaseActorType, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { - checkArguments(*entity.EntityImpl, ucEnumType) + checkUCArguments(actor, ucEnumType) ucManager := entity.Device().UseCaseManager() ucManager.Add(actor, ucEnumType, useCaseVersion, useCaseAvailable, scenarioSupport) @@ -68,12 +70,16 @@ func NewUseCaseWithActor(entity *EntityLocalImpl, actor model.UseCaseActorType, } } -func checkArguments(entity EntityImpl, ucEnumType model.UseCaseNameType) { +// check if there is an predefined mapping available +func checkEntityArguments(entity EntityImpl) { actor := entityTypeActorMap[entity.EntityType()] if actor == "" { panic(fmt.Errorf("cannot derive actor for entity type '%s'", entity.EntityType())) } +} +// check if the actor is valid for the given usecase type +func checkUCArguments(actor model.UseCaseActorType, ucEnumType model.UseCaseNameType) { if !linq.From(useCaseValidActorsMap[ucEnumType]).Contains(actor) { panic(fmt.Errorf("the actor '%s' is not valid for the use case '%s'", actor, ucEnumType)) } From c70de0c9bd51135b4ad972a8846b7861caabd1b8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 11 Jun 2023 16:07:26 +0200 Subject: [PATCH 029/240] Update usecases, actors, entitytypes --- spine/model/commondatatypes.go | 2 +- spine/model/usecaseinformation.go | 3 ++ spine/usecase.go | 68 +++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/spine/model/commondatatypes.go b/spine/model/commondatatypes.go index dd19e52d..b77f32aa 100644 --- a/spine/model/commondatatypes.go +++ b/spine/model/commondatatypes.go @@ -633,7 +633,7 @@ const ( EntityTypeTypeSmartEnergyAppliance EntityTypeType = "SmartEnergyAppliance" EntityTypeTypeSolarDHWStorage EntityTypeType = "SolarDHWStorage" EntityTypeTypeSolarThermalCircuit EntityTypeType = "SolarThermalCircuit" - EntityTypeTypeSubmeterElectricity EntityTypeType = "SubMeterElectricity" + EntityTypeTypeSubMeterElectricity EntityTypeType = "SubMeterElectricity" EntityTypeTypeTemperatureSensor EntityTypeType = "TemperatureSensor" EntityTypeTypeWasher EntityTypeType = "Washer" EntityTypeTypeBatterySystem EntityTypeType = "BatterySystem" diff --git a/spine/model/usecaseinformation.go b/spine/model/usecaseinformation.go index 0426479e..e2b9043b 100644 --- a/spine/model/usecaseinformation.go +++ b/spine/model/usecaseinformation.go @@ -26,6 +26,7 @@ const ( UseCaseActorTypeOutdoorTemperatureSensor UseCaseActorType = "OutdoorTemperatureSensor" UseCaseActorTypePVString UseCaseActorType = "PVString" UseCaseActorTypePVSystem UseCaseActorType = "PVSystem" + UseCaseActorTypeSmartAppliance UseCaseActorType = "SmartAppliance" UseCaseActorTypeVisualizationAppliance UseCaseActorType = "VisualizationAppliance" ) @@ -44,6 +45,8 @@ const ( UseCaseNameTypeEVCommissioningAndConfiguration UseCaseNameType = "evCommissioningAndConfiguration" UseCaseNameTypeEVSECommissioningAndConfiguration UseCaseNameType = "evseCommissioningAndConfiguration" UseCaseNameTypeEVStateOfCharge UseCaseNameType = "evStateOfCharge" + UseCaseNameTypeFlexibleLoad UseCaseNameType = "flexibleLoad" + UseCaseNameTypeFlexibleStartForWhiteGoods UseCaseNameType = "flexibleStartForWhiteGoods" UseCaseNameTypeLimitationOfPowerConsumption UseCaseNameType = "limitationOfPowerConsumption" UseCaseNameTypeLimitationOfPowerProduction UseCaseNameType = "limitationOfPowerProduction" UseCaseNameTypeIncentiveTableBasedPowerConsumptionManagement UseCaseNameType = "incentiveTableBasedPowerConsumptionManagement" diff --git a/spine/usecase.go b/spine/usecase.go index 09e81ecc..880873be 100644 --- a/spine/usecase.go +++ b/spine/usecase.go @@ -7,29 +7,63 @@ import ( "github.com/enbility/eebus-go/spine/model" ) +// a default mapping of a given EntityTypeType to a UseCaseActorType var entityTypeActorMap = map[model.EntityTypeType]model.UseCaseActorType{ - model.EntityTypeTypeEV: model.UseCaseActorTypeEV, - model.EntityTypeTypeEVSE: model.UseCaseActorTypeEVSE, + model.EntityTypeTypeBattery: model.UseCaseActorTypeBattery, model.EntityTypeTypeCEM: model.UseCaseActorTypeCEM, - model.EntityTypeTypeGridConnectionPointOfPremises: model.UseCaseActorTypeMonitoringAppliance, + model.EntityTypeTypeCompressor: model.UseCaseActorTypeCompressor, model.EntityTypeTypeElectricityStorageSystem: model.UseCaseActorTypeBatterySystem, model.EntityTypeTypeElectricityGenerationSystem: model.UseCaseActorTypePVSystem, + model.EntityTypeTypeEV: model.UseCaseActorTypeEV, + model.EntityTypeTypeEVSE: model.UseCaseActorTypeEVSE, + model.EntityTypeTypeDHWCircuit: model.UseCaseActorTypeDHWCircuit, + model.EntityTypeTypeHeatingCircuit: model.UseCaseActorTypeHeatingCircuit, + model.EntityTypeTypeHeatPumpAppliance: model.UseCaseActorTypeHeatPump, + model.EntityTypeTypeHvacRoom: model.UseCaseActorTypeHVACRoom, + model.EntityTypeTypeInverter: model.UseCaseActorTypeInverter, + model.EntityTypeTypeSmartEnergyAppliance: model.UseCaseActorTypeControllableSystem, + model.EntityTypeTypeSubMeterElectricity: model.UseCaseActorTypeControllableSystem, + model.EntityTypeTypeGridConnectionPointOfPremises: model.UseCaseActorTypeGridConnectionPoint, } +// list of known use cases and the allowed actors for each var useCaseValidActorsMap = map[model.UseCaseNameType][]model.UseCaseActorType{ - model.UseCaseNameTypeCoordinatedEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeEVSECommissioningAndConfiguration: {model.UseCaseActorTypeEVSE, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeEVChargingSummary: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeEVCommissioningAndConfiguration: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeEVStateOfCharge: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeMonitoringOfPowerConsumption: {model.UseCaseActorTypeCEM, model.UseCaseActorTypeHeatPump}, - model.UseCaseNameTypeMonitoringAndControlOfSmartGridReadyConditions: {model.UseCaseActorTypeCEM, model.UseCaseActorTypeHeatPump}, - model.UseCaseNameTypeMonitoringOfGridConnectionPoint: {model.UseCaseActorTypeCEM, model.UseCaseActorTypeMonitoringAppliance}, - model.UseCaseNameTypeVisualizationOfAggregatedBatteryData: {model.UseCaseActorTypeCEM, model.UseCaseActorTypeBatterySystem, model.UseCaseActorTypeVisualizationAppliance}, - model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData: {model.UseCaseActorTypeCEM, model.UseCaseActorTypePVSystem, model.UseCaseActorTypeVisualizationAppliance}, + model.UseCaseNameTypeConfigurationOfDhwSystemFunction: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeDHWCircuit}, + model.UseCaseNameTypeConfigurationOfDhwTemperature: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeDHWCircuit}, + model.UseCaseNameTypeConfigurationOfRoomCoolingSystemFunction: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeHVACRoom}, + model.UseCaseNameTypeConfigurationOfRoomCoolingTemperature: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeHVACRoom}, + model.UseCaseNameTypeConfigurationOfRoomHeatingSystemFunction: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeHVACRoom}, + model.UseCaseNameTypeConfigurationOfRoomHeatingTemperature: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeHVACRoom}, + model.UseCaseNameTypeControlOfBattery: {model.UseCaseActorTypeInverter, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeCoordinatedEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM, model.UseCaseActorTypeEnergyBroker}, + model.UseCaseNameTypeEVChargingSummary: {model.UseCaseActorTypeEVSE, model.UseCaseActorTypeCEM, model.UseCaseActorTypeEnergyBroker}, + model.UseCaseNameTypeEVCommissioningAndConfiguration: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeEVSECommissioningAndConfiguration: {model.UseCaseActorTypeEVSE, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeEVStateOfCharge: {model.UseCaseActorTypeEV, model.UseCaseActorTypeMonitoringAppliance}, + model.UseCaseNameTypeFlexibleLoad: {model.UseCaseActorTypeEnergyConsumer, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeFlexibleStartForWhiteGoods: {model.UseCaseActorTypeSmartAppliance, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeIncentiveTableBasedPowerConsumptionManagement: {model.UseCaseActorTypeEnergyConsumer, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeLimitationOfPowerConsumption: {model.UseCaseActorTypeEnergyGuard, model.UseCaseActorTypeControllableSystem}, + model.UseCaseNameTypeLimitationOfPowerProduction: {model.UseCaseActorTypeEnergyGuard, model.UseCaseActorTypeControllableSystem}, + model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeMonitoringAndControlOfSmartGridReadyConditions: {model.UseCaseActorTypeHeatPump, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeMonitoringOfBattery: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeBattery}, + model.UseCaseNameTypeMonitoringOfDhwSystemFunction: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeDHWCircuit}, + model.UseCaseNameTypeMonitoringOfDhwTemperature: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeDHWCircuit}, + model.UseCaseNameTypeMonitoringOfGridConnectionPoint: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeGridConnectionPoint}, + model.UseCaseNameTypeMonitoringOfInverter: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeInverter}, + model.UseCaseNameTypeMonitoringOfOutdoorTemperature: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeOutdoorTemperatureSensor}, + model.UseCaseNameTypeMonitoringOfPowerConsumption: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeMonitoredUnit}, + model.UseCaseNameTypeMonitoringOfPvString: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypePVString}, + model.UseCaseNameTypeMonitoringOfRoomCoolingSystemFunction: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeHVACRoom}, + model.UseCaseNameTypeMonitoringOfRoomHeatingSystemFunction: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeHVACRoom}, + model.UseCaseNameTypeMonitoringOfRoomTemperature: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeHVACRoom}, + model.UseCaseNameTypeOptimizationOfSelfConsumptionByHeatPumpCompressorFlexibility: {model.UseCaseActorTypeCompressor, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, + model.UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM, model.UseCaseActorTypeEnergyGuard}, + model.UseCaseNameTypeVisualizationOfAggregatedBatteryData: {model.UseCaseActorTypeVisualizationAppliance, model.UseCaseActorTypeBatterySystem}, + model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData: {model.UseCaseActorTypeVisualizationAppliance, model.UseCaseActorTypePVSystem}, + model.UseCaseNameTypeVisualizationOfHeatingAreaName: {model.UseCaseActorTypeVisualizationAppliance, model.UseCaseActorTypeHeatingCircuit, model.UseCaseActorTypeHeatingZone, model.UseCaseActorTypeHVACRoom}, } // defines a specific usecase implementation @@ -44,7 +78,7 @@ type UseCaseImpl struct { scenarioSupport []model.UseCaseScenarioSupportType } -// returns a UseCaseImpl with a default mapping of entity to actor +// returns a UseCaseImpl with a default mapping of entity to actor using data func NewUseCase(entity *EntityLocalImpl, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { checkEntityArguments(*entity.EntityImpl) From 01d4ca66a5b17348a4a360ab5348dfc7c6a0807b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 11 Jun 2023 16:15:39 +0200 Subject: [PATCH 030/240] Fix missing connect attempt in RegisterRemoteSKI --- service/hub.go | 1 + 1 file changed, 1 insertion(+) diff --git a/service/hub.go b/service/hub.go index b0467b4a..2d852dfa 100644 --- a/service/hub.go +++ b/service/hub.go @@ -626,6 +626,7 @@ func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { service.Trusted = enable if enable { + h.checkRestartMdnsSearch() return } From 2e426b8357fd42e8e096ccd6fe0d3ac9f438fbc4 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 12 Jun 2023 12:56:07 +0200 Subject: [PATCH 031/240] Provide entity description --- spine/entity.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spine/entity.go b/spine/entity.go index 03396314..b80abf37 100644 --- a/spine/entity.go +++ b/spine/entity.go @@ -44,6 +44,10 @@ func (r *EntityImpl) Address() *model.EntityAddressType { return r.address } +func (r *EntityImpl) Description() *model.DescriptionType { + return r.description +} + func (r *EntityImpl) SetDescription(d *model.DescriptionType) { r.description = d } From df73da659588dcd122fafb8859239fee5ad1736e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 13 Jun 2023 15:04:31 +0200 Subject: [PATCH 032/240] Fix closing connection on removing pairing --- ship/connection.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ship/connection.go b/ship/connection.go index c4a11b70..25deb6b4 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -145,8 +145,15 @@ func (c *ShipConnection) CloseConnection(safe bool, code int, reason string) { c.removeRemoteDeviceConnection() + // handshake is completed if approved or aborted + state := c.getState() + handshakeEnd := state == SmeStateComplete || + state == SmeHelloStateAbortDone || + state == SmeHelloStateRemoteAbortDone || + state == SmeHelloStateRejected + // this may not be used for Connection Data Exchange is entered! - if safe && c.getState() == SmeStateComplete { + if safe && state == SmeStateComplete { // SHIP 13.4.7: Connection Termination Announce closeMessage := model.ConnectionClose{ ConnectionClose: model.ConnectionCloseType{ @@ -157,7 +164,8 @@ func (c *ShipConnection) CloseConnection(safe bool, code int, reason string) { _ = c.sendShipModel(model.MsgTypeEnd, closeMessage) - if c.getState() != SmeStateError { + if state != SmeStateError { + c.serviceDataProvider.HandleConnectionClosed(c, handshakeEnd) return } } @@ -168,13 +176,6 @@ func (c *ShipConnection) CloseConnection(safe bool, code int, reason string) { } c.DataHandler.CloseDataConnection(closeCode, reason) - // handshake is completed if approved or aborted - state := c.getState() - handshakeEnd := state == SmeStateComplete || - state == SmeHelloStateAbortDone || - state == SmeHelloStateRemoteAbortDone || - state == SmeHelloStateRejected - c.serviceDataProvider.HandleConnectionClosed(c, handshakeEnd) }) } From cb72f48f04a1832d3619922644ea977ee39e43c2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 2 Jul 2023 15:01:07 +0200 Subject: [PATCH 033/240] Update zerconf to custom fork This fork adds support for removed and expired entries --- go.mod | 2 +- go.sum | 4 ++-- service/mdns.go | 17 +++++++++++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index db499e08..6bfa8fbe 100644 --- a/go.mod +++ b/go.mod @@ -17,12 +17,12 @@ require ( ) require ( + github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f github.com/ahmetb/go-linq/v3 v3.2.0 github.com/godbus/dbus/v5 v5.1.0 github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.0 github.com/holoplot/go-avahi v1.0.1 - github.com/libp2p/zeroconf/v2 v2.2.0 github.com/rickb777/date v1.20.1 github.com/stretchr/testify v1.8.2 gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a diff --git a/go.sum b/go.sum index 10796d4a..d2422101 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f h1:3+usd0uwuU/PnxDgKiQFc3FSR0C1PnV5Bk03NUKktaU= +github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f/go.mod h1:Fhz5FR1v8Nv4Hj1v9oGXDz4C33+ZaAPqK+Y2wpfWvu0= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -14,8 +16,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk= github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= -github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= -github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= diff --git a/service/mdns.go b/service/mdns.go index c1a068c1..e5dff2e6 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -10,11 +10,11 @@ import ( "sync" "syscall" + "github.com/DerAndereAndi/zeroconf/v2" "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/util" "github.com/godbus/dbus/v5" "github.com/holoplot/go-avahi" - "github.com/libp2p/zeroconf/v2" ) type MdnsEntry struct { @@ -311,7 +311,9 @@ func (m *mdns) resolveEntries() { var avBrowser *avahi.ServiceBrowser zcEntries := make(chan *zeroconf.ServiceEntry) + zcRemoved := make(chan *zeroconf.ServiceEntry) defer close(zcEntries) + defer close(zcRemoved) if m.av != nil { // instead of limiting search on specific allowed interfaces, we allow all and filter the results @@ -321,7 +323,7 @@ func (m *mdns) resolveEntries() { } } else { go func() { - _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries) + _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries, zcRemoved) }() } @@ -344,6 +346,17 @@ func (m *mdns) resolveEntries() { break case <-m.cancelChan: ctx.Done() + case service := <-zcRemoved: + // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records + if len(service.Text) == 0 { + continue + } + + elements := m.parseTxt(service.Text) + + addresses := service.AddrIPv4 + m.processMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, true) + case service := <-zcEntries: // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records if len(service.Text) == 0 { From bd79e8033cdd786717b1e6bf7579fd0950bc3d82 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 4 Jul 2023 13:49:31 +0200 Subject: [PATCH 034/240] Do not automatically shut down mDNS on connect The stack should not decide wether the mDNS service should be shutdown on non CEM systems. For sure not at this stage. Because that leads the remote system to not seing this service and preventing it from pairing if it did not pair yet. --- service/hub.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/service/hub.go b/service/hub.go index 2d852dfa..7a9abc6f 100644 --- a/service/hub.go +++ b/service/hub.go @@ -342,12 +342,6 @@ func (h *connectionsHub) registerConnection(connection *ship.ShipConnection) { h.muxCon.Lock() h.connections[connection.RemoteSKI] = connection h.muxCon.Unlock() - - // shutdown mDNS if this is not a CEM - if h.localService.DeviceType != model.DeviceTypeTypeEnergyManagementSystem { - h.mdns.UnannounceMdnsEntry() - h.mdns.UnregisterMdnsSearch(h) - } } // return the connection for a specific SKI From 4ceb14e9d3d542451e3a9bc7d260186bf2aff396 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 4 Jul 2023 15:06:58 +0200 Subject: [PATCH 035/240] Set the default TTL to 120s using go-zeroconf The default on avahi is already 120s --- service/mdns.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/mdns.go b/service/mdns.go index e5dff2e6..20f8ada4 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -188,7 +188,8 @@ func (m *mdns) AnnounceMdnsEntry() error { if m.av == nil { logging.Log.Debug("mdns: using zeroconf") // use Zeroconf library if avahi is not available - mDNSServer, err := zeroconf.Register(serviceName, shipZeroConfServiceType, shipZeroConfDomain, m.configuration.port, txt, ifaces) + // Set TTL to 2 minutes as defined in SHIP chapter 7 + mDNSServer, err := zeroconf.Register(serviceName, shipZeroConfServiceType, shipZeroConfDomain, m.configuration.port, txt, ifaces, zeroconf.TTL(120)) if err == nil { m.zc = mDNSServer From 1df60dde386ca1be147f7231ac76ead39901f7c1 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 4 Jul 2023 15:12:42 +0200 Subject: [PATCH 036/240] Demo clients trust handling updates - Only allow connections if the requesting SKI is the registered one - Quit the process if the registered remote SKI denied trust --- cmd/evse/main.go | 15 ++++++++++++--- cmd/hems/main.go | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/cmd/evse/main.go b/cmd/evse/main.go index fb3ac77e..a9a92388 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -17,6 +17,8 @@ import ( "github.com/enbility/eebus-go/spine/model" ) +var remoteSki string + type evse struct { myService *service.EEBUSService } @@ -24,7 +26,6 @@ type evse struct { func (h *evse) run() { var err error var certificate tls.Certificate - var remoteSki string if len(os.Args) == 5 { remoteSki = os.Args[2] @@ -97,9 +98,17 @@ func (h *evse) VisibleRemoteServicesUpdated(service *service.EEBUSService, entri func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *evse) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) {} +func (h *evse) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) { + if ski == remoteSki && detail.State == service.ConnectionStateRemoteDeniedTrust { + fmt.Println("The remote service denied trust. Exiting.") + h.myService.Shutdown() + os.Exit(0) + } +} -func (h *evse) AllowWaitingForTrust(ski string) bool { return true } +func (h *evse) AllowWaitingForTrust(ski string) bool { + return ski == remoteSki +} // main app func usage() { diff --git a/cmd/hems/main.go b/cmd/hems/main.go index 19a5a0cf..a69a33ba 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -17,6 +17,8 @@ import ( "github.com/enbility/eebus-go/spine/model" ) +var remoteSki string + type hems struct { myService *service.EEBUSService } @@ -24,7 +26,6 @@ type hems struct { func (h *hems) run() { var err error var certificate tls.Certificate - var remoteSki string if len(os.Args) == 5 { remoteSki = os.Args[2] @@ -97,9 +98,17 @@ func (h *hems) VisibleRemoteServicesUpdated(service *service.EEBUSService, entri func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *hems) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) {} +func (h *hems) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) { + if ski == remoteSki && detail.State == service.ConnectionStateRemoteDeniedTrust { + fmt.Println("The remote service denied trust. Exiting.") + h.myService.Shutdown() + os.Exit(0) + } +} -func (h *hems) AllowWaitingForTrust(ski string) bool { return true } +func (h *hems) AllowWaitingForTrust(ski string) bool { + return ski == remoteSki +} // UCEvseCommisioningConfigurationCemDelegate From fda1164d939a2ce25f7e16a53a49e772440b219e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 4 Jul 2023 16:40:51 +0200 Subject: [PATCH 037/240] Improve function description --- service/service.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/service.go b/service/service.go index 2e1ff909..80d843f1 100644 --- a/service/service.go +++ b/service/service.go @@ -280,8 +280,8 @@ func (s *EEBUSService) RemoteServiceForSKI(ski string) *ServiceDetails { return s.connectionsHub.serviceForSKI(ski) } -// Adds a new device to the list of known devices which can be connected to -// and connect it if it is currently not connected +// Sets the SKI as being paired or not +// and connect it if paired and not currently being connected func (s *EEBUSService) RegisterRemoteSKI(ski string, enable bool) { s.connectionsHub.RegisterRemoteSKI(ski, enable) } From 83d84f193f924e3d71306aaa421204ac01f79a1e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 4 Jul 2023 16:42:11 +0200 Subject: [PATCH 038/240] Improve connection handling - Start websocket server before announcing service - Only invoke `ListenAndServeTLS` in a go routine - Do not invoke mDNS Announcement twice --- service/hub.go | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/service/hub.go b/service/hub.go index 7a9abc6f..06b5f98e 100644 --- a/service/hub.go +++ b/service/hub.go @@ -119,22 +119,16 @@ func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineL // start the ConnectionsHub with all its services func (h *connectionsHub) start() { + // start the websocket server + if err := h.startWebsocketServer(); err != nil { + logging.Log.Debug("error during websocket server starting:", err) + } + // start mDNS err := h.mdns.SetupMdnsService() if err != nil { logging.Log.Debug("error during mdns setup:", err) } - - // start the websocket server - go func() { - if err := h.startWebsocketServer(); err != nil { - logging.Log.Debug("error during websocket server starting:", err) - } - }() - - if err := h.mdns.AnnounceMdnsEntry(); err != nil { - logging.Log.Debug("error registering mDNS Service:", err) - } } var _ ship.ShipServiceDataProvider = (*connectionsHub)(nil) @@ -410,9 +404,12 @@ func (h *connectionsHub) startWebsocketServer() error { }, } - if err := h.httpServer.ListenAndServeTLS("", ""); err != nil { - return err - } + go func() { + if err := h.httpServer.ListenAndServeTLS("", ""); err != nil { + logging.Log.Debug("websocket server error:", err) + // TODO: decide how to handle this case + } + }() return nil } From c9b602f2bbd99a63da85d3f50aa3f72b4fbad90f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 4 Jul 2023 16:43:20 +0200 Subject: [PATCH 039/240] Improve demo cmds trust denial handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Unregister the remote SKI, as pairing was denied, so it should be locally removed as well. This makes sure the client doesn’t try to reconnect instantly - Cancel the SHIP pairing process --- cmd/evse/main.go | 2 ++ cmd/hems/main.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/cmd/evse/main.go b/cmd/evse/main.go index a9a92388..1f2f931d 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -101,6 +101,8 @@ func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} func (h *evse) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) { if ski == remoteSki && detail.State == service.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") + h.myService.RegisterRemoteSKI(ski, false) + h.myService.CancelPairingWithSKI(ski) h.myService.Shutdown() os.Exit(0) } diff --git a/cmd/hems/main.go b/cmd/hems/main.go index a69a33ba..d490fa35 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -101,6 +101,8 @@ func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} func (h *hems) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) { if ski == remoteSki && detail.State == service.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") + h.myService.RegisterRemoteSKI(ski, false) + h.myService.CancelPairingWithSKI(ski) h.myService.Shutdown() os.Exit(0) } From feb61c498d2200b8222cfc287356bfb8e8d5a8fe Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 4 Jul 2023 17:35:03 +0200 Subject: [PATCH 040/240] Register remote SKI ahead start This ensures that the remote device connection is accepted and also connected to as early as possible --- cmd/evse/main.go | 4 ++-- cmd/hems/main.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 1f2f931d..29d68d33 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -81,10 +81,10 @@ func (h *evse) run() { os.Exit(0) } + h.myService.RegisterRemoteSKI(remoteSki, true) + h.myService.Start() // defer h.myService.Shutdown() - - h.myService.RegisterRemoteSKI(remoteSki, true) } // EEBUSServiceHandler diff --git a/cmd/hems/main.go b/cmd/hems/main.go index d490fa35..c4730083 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -81,10 +81,10 @@ func (h *hems) run() { os.Exit(0) } + h.myService.RegisterRemoteSKI(remoteSki, true) + h.myService.Start() // defer h.myService.Shutdown() - - h.myService.RegisterRemoteSKI(remoteSki, true) } // EEBUSServiceHandler From 140d8ad199791879bbfd25d84bec6a1f638d57d7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 6 Jul 2023 12:28:23 +0200 Subject: [PATCH 041/240] Delete unused mocks --- service/mocks/MdnsSearch.go | 33 ---------------- service/mocks/MdnsService.go | 76 ------------------------------------ 2 files changed, 109 deletions(-) delete mode 100644 service/mocks/MdnsSearch.go delete mode 100644 service/mocks/MdnsService.go diff --git a/service/mocks/MdnsSearch.go b/service/mocks/MdnsSearch.go deleted file mode 100644 index 49a6785d..00000000 --- a/service/mocks/MdnsSearch.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. - -package mocks - -import ( - service "github.com/enbility/eebus-go/service" - mock "github.com/stretchr/testify/mock" -) - -// MdnsSearch is an autogenerated mock type for the MdnsSearch type -type MdnsSearch struct { - mock.Mock -} - -// ReportMdnsEntries provides a mock function with given fields: entries -func (_m *MdnsSearch) ReportMdnsEntries(entries map[string]service.MdnsEntry) { - _m.Called(entries) -} - -type mockConstructorTestingTNewMdnsSearch interface { - mock.TestingT - Cleanup(func()) -} - -// NewMdnsSearch creates a new instance of MdnsSearch. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMdnsSearch(t mockConstructorTestingTNewMdnsSearch) *MdnsSearch { - mock := &MdnsSearch{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/service/mocks/MdnsService.go b/service/mocks/MdnsService.go deleted file mode 100644 index 780ac721..00000000 --- a/service/mocks/MdnsService.go +++ /dev/null @@ -1,76 +0,0 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. - -package mocks - -import ( - service "github.com/enbility/eebus-go/service" - mock "github.com/stretchr/testify/mock" -) - -// MdnsService is an autogenerated mock type for the MdnsService type -type MdnsService struct { - mock.Mock -} - -// AnnounceMdnsEntry provides a mock function with given fields: -func (_m *MdnsService) AnnounceMdnsEntry() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RegisterMdnsSearch provides a mock function with given fields: cb -func (_m *MdnsService) RegisterMdnsSearch(cb service.MdnsSearch) { - _m.Called(cb) -} - -// SetupMdnsService provides a mock function with given fields: -func (_m *MdnsService) SetupMdnsService() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ShutdownMdnsService provides a mock function with given fields: -func (_m *MdnsService) ShutdownMdnsService() { - _m.Called() -} - -// UnannounceMdnsEntry provides a mock function with given fields: -func (_m *MdnsService) UnannounceMdnsEntry() { - _m.Called() -} - -// UnregisterMdnsSearch provides a mock function with given fields: cb -func (_m *MdnsService) UnregisterMdnsSearch(cb service.MdnsSearch) { - _m.Called(cb) -} - -type mockConstructorTestingTNewMdnsService interface { - mock.TestingT - Cleanup(func()) -} - -// NewMdnsService creates a new instance of MdnsService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMdnsService(t mockConstructorTestingTNewMdnsService) *MdnsService { - mock := &MdnsService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} From e78a80e90162e6b2a570162e802d14e09b088114 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 13 Jul 2023 10:21:13 +0200 Subject: [PATCH 042/240] Fix typo and duplicate setting --- service/hub.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/service/hub.go b/service/hub.go index 06b5f98e..d5ca36ea 100644 --- a/service/hub.go +++ b/service/hub.go @@ -610,8 +610,8 @@ func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { } // Sets the SKI as being paired or not -// Should be used for services which completed the pairing process and where -// stored as having the process completed +// Should be used for services which completed the pairing process and +// which were stored as having the process completed func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { service := h.serviceForSKI(ski) service.Trusted = enable @@ -624,7 +624,6 @@ func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { h.removeConnectionAttemptCounter(ski) service.ConnectionStateDetail.State = ConnectionStateNone - service.Trusted = false h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) From 5e32c35c3f74449d330fd1ae1f9b0c9b6e1bb5bc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 13 Jul 2023 18:26:24 +0200 Subject: [PATCH 043/240] Add callback feature to process result msgs --- features/feature.go | 5 +++++ spine/feature_local.go | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/features/feature.go b/features/feature.go index b47e54f2..5f37f081 100644 --- a/features/feature.go +++ b/features/feature.go @@ -9,6 +9,7 @@ import ( type Feature interface { SubscribeForEntity() error + AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg spine.ResultMessage)) } type FeatureImpl struct { @@ -53,6 +54,10 @@ func (f *FeatureImpl) SubscribeForEntity() error { return nil } +func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg spine.ResultMessage)) { + f.featureLocal.AddResultCallback(msgCounterReference, function) +} + // bind to the feature of a the entity func (f *FeatureImpl) Bind() error { if _, fErr := f.featureLocal.Bind(f.featureRemote.Device(), f.featureRemote.Address()); fErr != nil { diff --git a/spine/feature_local.go b/spine/feature_local.go index 11225c1a..6ddc6c4a 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -2,6 +2,7 @@ package spine import ( "fmt" + "sync" "time" "github.com/enbility/eebus-go/logging" @@ -14,6 +15,7 @@ type FeatureLocal interface { Data(function model.FunctionType) any SetData(function model.FunctionType, data any) AddResultHandler(handler FeatureResult) + AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg ResultMessage)) Information() *model.NodeManagementDetailedDiscoveryFeatureInformationType AddFunctionType(function model.FunctionType, read, write bool) RequestData( @@ -61,10 +63,13 @@ var _ FeatureLocal = (*FeatureLocalImpl)(nil) type FeatureLocalImpl struct { *FeatureImpl + + muxResultCB sync.Mutex entity *EntityLocalImpl functionDataMap map[model.FunctionType]FunctionDataCmd pendingRequests PendingRequests resultHandler []FeatureResult + resultCallback map[model.MsgCounterType]func(result ResultMessage) } func NewFeatureLocalImpl(id uint, entity *EntityLocalImpl, ftype model.FeatureTypeType, role model.RoleType) *FeatureLocalImpl { @@ -76,6 +81,7 @@ func NewFeatureLocalImpl(id uint, entity *EntityLocalImpl, ftype model.FeatureTy entity: entity, functionDataMap: make(map[model.FunctionType]FunctionDataCmd), pendingRequests: NewPendingRequest(), + resultCallback: make(map[model.MsgCounterType]func(result ResultMessage)), } for _, fd := range CreateFunctionData[FunctionDataCmd](ftype) { @@ -120,6 +126,27 @@ func (r *FeatureLocalImpl) AddResultHandler(handler FeatureResult) { r.resultHandler = append(r.resultHandler, handler) } +func (r *FeatureLocalImpl) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg ResultMessage)) { + r.muxResultCB.Lock() + defer r.muxResultCB.Unlock() + + r.resultCallback[msgCounterReference] = function +} + +func (r *FeatureLocalImpl) processResultCallbacks(msgCounterReference model.MsgCounterType, msg ResultMessage) { + r.muxResultCB.Lock() + defer r.muxResultCB.Unlock() + + cb, ok := r.resultCallback[msgCounterReference] + if !ok { + return + } + + go cb(msg) + + delete(r.resultCallback, msgCounterReference) +} + func (r *FeatureLocalImpl) Information() *model.NodeManagementDetailedDiscoveryFeatureInformationType { var funs []model.FunctionPropertyType for fun, operations := range r.operations { @@ -363,7 +390,7 @@ func (r *FeatureLocalImpl) processResult(message *Message) *ErrorType { // we don't need to populate this error as requests don't require a pendingRequest entry _ = r.pendingRequests.SetResult(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference, NewErrorTypeFromResult(message.Cmd.ResultData)) - if r.resultHandler == nil || message.RequestHeader.MsgCounterReference == nil { + if message.RequestHeader.MsgCounterReference == nil { return nil } @@ -376,10 +403,14 @@ func (r *FeatureLocalImpl) processResult(message *Message) *ErrorType { DeviceRemote: message.DeviceRemote, } - for _, item := range r.resultHandler { - go item.HandleResult(errorMsg) + if r.resultHandler != nil { + for _, item := range r.resultHandler { + go item.HandleResult(errorMsg) + } } + r.processResultCallbacks(*message.RequestHeader.MsgCounterReference, errorMsg) + return nil default: From 9a86db7fd679c898281b08ece2bc3646e7aa4c4b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 14 Jul 2023 13:53:45 +0200 Subject: [PATCH 044/240] Restructure some code and add a check --- service/mdns.go | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/service/mdns.go b/service/mdns.go index 20f8ada4..2f212ab6 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -316,21 +316,21 @@ func (m *mdns) resolveEntries() { defer close(zcEntries) defer close(zcRemoved) + var end bool + if m.av != nil { // instead of limiting search on specific allowed interfaces, we allow all and filter the results if avBrowser, err = m.av.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0); err != nil { logging.Log.Debug("mdns: error setting up avahi browser:", err) return } - } else { - go func() { - _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries, zcRemoved) - }() - } - var end bool - for !end { - if m.av != nil { + if avBrowser == nil { + logging.Log.Debug("mdns: avahi browser is not available") + return + } + + for !end { select { case <-m.cancelChan: end = true @@ -340,7 +340,16 @@ func (m *mdns) resolveEntries() { case service := <-avBrowser.RemoveChannel: m.processAvahiService(service, true) } - } else { + } + + m.av.ServiceBrowserFree(avBrowser) + } else { + + go func() { + _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries, zcRemoved) + }() + + for !end { select { case <-ctx.Done(): end = true @@ -374,10 +383,6 @@ func (m *mdns) resolveEntries() { } } - if m.av != nil { - m.av.ServiceBrowserFree(avBrowser) - } - m.mux.Lock() m.isSearchingServices = false m.mux.Unlock() From 30ac2b843d92d39fa9a15f2daa04bf9664d6ccbb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 14 Jul 2023 14:28:32 +0200 Subject: [PATCH 045/240] Add checking for avahi browser availability If the avahi browser is not available on setup, the code can fallback to use the go zeroconf implementation --- service/mdns.go | 157 +++++++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 68 deletions(-) diff --git a/service/mdns.go b/service/mdns.go index 2f212ab6..7b4e79ed 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "net" "os" @@ -126,7 +127,17 @@ func (m *mdns) setupAvahi() (*avahi.Server, error) { return nil, err } - return avahiServer, nil + avBrowser, err := m.av.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0) + if err != nil { + return nil, err + } + + if avBrowser != nil { + m.av.ServiceBrowserFree(avBrowser) + return avahiServer, nil + } + + return nil, errors.New("avahi service is not working as expected") } // Return allowed interfaces for mDNS @@ -274,7 +285,7 @@ func (m *mdns) RegisterMdnsSearch(cb MdnsSearch) { m.isSearchingServices = true m.mux.Unlock() logging.Log.Debug("mdns: start search") - go m.resolveEntries() + m.resolveEntries() return } @@ -301,91 +312,101 @@ func (m *mdns) UnregisterMdnsSearch(cb MdnsSearch) { m.stopResolvingEntries() } -// search for mDNS entries and report them -// to be invoked in a background thread! -func (m *mdns) resolveEntries() { - // for Zeroconf we need a context - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - +func (m *mdns) resolveEntriesAvahi() { var err error - var avBrowser *avahi.ServiceBrowser + var end bool - zcEntries := make(chan *zeroconf.ServiceEntry) - zcRemoved := make(chan *zeroconf.ServiceEntry) - defer close(zcEntries) - defer close(zcRemoved) + var avBrowser *avahi.ServiceBrowser - var end bool + // instead of limiting search on specific allowed interfaces, we allow all and filter the results + if avBrowser, err = m.av.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0); err != nil { + logging.Log.Debug("mdns: error setting up avahi browser:", err) + return + } - if m.av != nil { - // instead of limiting search on specific allowed interfaces, we allow all and filter the results - if avBrowser, err = m.av.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0); err != nil { - logging.Log.Debug("mdns: error setting up avahi browser:", err) - return - } + if avBrowser == nil { + logging.Log.Debug("mdns: avahi browser is not available") + return + } - if avBrowser == nil { - logging.Log.Debug("mdns: avahi browser is not available") - return + for !end { + select { + case <-m.cancelChan: + end = true + break + case service := <-avBrowser.AddChannel: + m.processAvahiService(service, false) + case service := <-avBrowser.RemoveChannel: + m.processAvahiService(service, true) } + } - for !end { - select { - case <-m.cancelChan: - end = true - break - case service := <-avBrowser.AddChannel: - m.processAvahiService(service, false) - case service := <-avBrowser.RemoveChannel: - m.processAvahiService(service, true) - } - } + m.av.ServiceBrowserFree(avBrowser) +} - m.av.ServiceBrowserFree(avBrowser) - } else { +func (m *mdns) resolveEntriesZeroconf() { + var end bool - go func() { - _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries, zcRemoved) - }() + zcEntries := make(chan *zeroconf.ServiceEntry) + zcRemoved := make(chan *zeroconf.ServiceEntry) + defer close(zcEntries) + defer close(zcRemoved) - for !end { - select { - case <-ctx.Done(): - end = true - break - case <-m.cancelChan: - ctx.Done() - case service := <-zcRemoved: - // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records - if len(service.Text) == 0 { - continue - } + // for Zeroconf we need a context + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - elements := m.parseTxt(service.Text) + go func() { + _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries, zcRemoved) + }() - addresses := service.AddrIPv4 - m.processMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, true) + for !end { + select { + case <-ctx.Done(): + end = true + break + case <-m.cancelChan: + ctx.Done() + case service := <-zcRemoved: + // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records + if len(service.Text) == 0 { + continue + } - case service := <-zcEntries: - // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records - if len(service.Text) == 0 { - continue - } + elements := m.parseTxt(service.Text) - elements := m.parseTxt(service.Text) + addresses := service.AddrIPv4 + m.processMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, true) - addresses := service.AddrIPv4 - // Only use IPv4 for now - // addresses = append(addresses, service.AddrIPv6...) - m.processMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, false) + case service := <-zcEntries: + // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records + if len(service.Text) == 0 { + continue } + + elements := m.parseTxt(service.Text) + + addresses := service.AddrIPv4 + // Only use IPv4 for now + // addresses = append(addresses, service.AddrIPv6...) + m.processMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, false) } } +} - m.mux.Lock() - m.isSearchingServices = false - m.mux.Unlock() +// search for mDNS entries and report them +func (m *mdns) resolveEntries() { + go func() { + if m.av != nil { + m.resolveEntriesAvahi() + } else { + m.resolveEntriesZeroconf() + } + + m.mux.Lock() + m.isSearchingServices = false + m.mux.Unlock() + }() } // stop searching for mDNS entries From a5a20b8927ce8b5d31bc2b1191d7298ce8ab9692 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 14 Jul 2023 16:27:11 +0200 Subject: [PATCH 046/240] Fix crash --- service/mdns.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/mdns.go b/service/mdns.go index 7b4e79ed..7b276db2 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -127,13 +127,13 @@ func (m *mdns) setupAvahi() (*avahi.Server, error) { return nil, err } - avBrowser, err := m.av.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0) + avBrowser, err := avahiServer.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0) if err != nil { return nil, err } if avBrowser != nil { - m.av.ServiceBrowserFree(avBrowser) + avahiServer.ServiceBrowserFree(avBrowser) return avahiServer, nil } From 92fb74e03f7708c0ac658a07162b64d428d73eaf Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 17 Jul 2023 15:41:28 +0200 Subject: [PATCH 047/240] Refactor mdns - rename `mdns` to `mdnsManager` - separate Avahi and Zeroconf into individual implementations using interfaces --- service/hub.go | 2 - service/mdns.go | 356 +++++++-------------------------------- service/mdns/avahi.go | 186 ++++++++++++++++++++ service/mdns/helper.go | 20 +++ service/mdns/types.go | 18 ++ service/mdns/zeroconf.go | 106 ++++++++++++ 6 files changed, 393 insertions(+), 295 deletions(-) create mode 100644 service/mdns/avahi.go create mode 100644 service/mdns/helper.go create mode 100644 service/mdns/types.go create mode 100644 service/mdns/zeroconf.go diff --git a/service/hub.go b/service/hub.go index d5ca36ea..1784cfb0 100644 --- a/service/hub.go +++ b/service/hub.go @@ -24,8 +24,6 @@ import ( const shipWebsocketSubProtocol = "ship" // SHIP 10.2: sub protocol is required for websocket connections const shipWebsocketPath = "/ship/" -const shipZeroConfServiceType = "_ship._tcp" -const shipZeroConfDomain = "local." // used for randomizing the connection initiation delay // this limits the possibility of concurrent connection attempts from both sides diff --git a/service/mdns.go b/service/mdns.go index 7b276db2..838b10d0 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -1,20 +1,17 @@ package service import ( - "context" "errors" "fmt" "net" "os" "os/signal" - "strings" "sync" "syscall" - "github.com/DerAndereAndi/zeroconf/v2" "github.com/enbility/eebus-go/logging" + "github.com/enbility/eebus-go/service/mdns" "github.com/enbility/eebus-go/util" - "github.com/godbus/dbus/v5" "github.com/holoplot/go-avahi" ) @@ -49,7 +46,7 @@ type MdnsService interface { UnregisterMdnsSearch(cb MdnsSearch) } -type mdns struct { +type mdnsManager struct { configuration *Configuration ski string @@ -64,18 +61,13 @@ type mdns struct { // the registered callback, only connectionsHub is using this searchDelegate MdnsSearch - // The zeroconf service for mDNS related tasks - zc *zeroconf.Server - - // The alternative avahi mDNS service - av *avahi.Server - avEntryGroup *avahi.EntryGroup + mdnsProvider mdns.MdnsProvider mux sync.Mutex } -func newMDNS(ski string, configuration *Configuration) *mdns { - m := &mdns{ +func newMDNS(ski string, configuration *Configuration) *mdnsManager { + m := &mdnsManager{ ski: ski, configuration: configuration, entries: make(map[string]MdnsEntry), @@ -85,63 +77,8 @@ func newMDNS(ski string, configuration *Configuration) *mdns { return m } -var _ MdnsService = (*mdns)(nil) - -func (m *mdns) SetupMdnsService() error { - - if av, err := m.setupAvahi(); err == nil { - m.av = av - } - - // on startup always start mDNS announcement - if err := m.AnnounceMdnsEntry(); err != nil { - return err - } - - // catch signals - go func() { - signalC := make(chan os.Signal, 1) - signal.Notify(signalC, os.Interrupt, syscall.SIGTERM) - - <-signalC // wait for signal - - m.UnannounceMdnsEntry() - }() - - return nil -} - -// setup avahi for mDNS -func (m *mdns) setupAvahi() (*avahi.Server, error) { - dbusConn, err := dbus.SystemBus() - if err != nil { - return nil, err - } - - avahiServer, err := avahi.ServerNew(dbusConn) - if err != nil { - return nil, err - } - - if _, err := avahiServer.GetAPIVersion(); err != nil { - return nil, err - } - - avBrowser, err := avahiServer.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0) - if err != nil { - return nil, err - } - - if avBrowser != nil { - avahiServer.ServiceBrowserFree(avBrowser) - return avahiServer, nil - } - - return nil, errors.New("avahi service is not working as expected") -} - // Return allowed interfaces for mDNS -func (m *mdns) interfaces() ([]net.Interface, []int32, error) { +func (m *mdnsManager) interfaces() ([]net.Interface, []int32, error) { var ifaces []net.Interface var ifaceIndexes []int32 @@ -166,19 +103,51 @@ func (m *mdns) interfaces() ([]net.Interface, []int32, error) { return ifaces, ifaceIndexes, nil } +var _ MdnsService = (*mdnsManager)(nil) + +func (m *mdnsManager) SetupMdnsService() error { + ifaces, ifaceIndexes, err := m.interfaces() + if err != nil { + return err + } + + m.mdnsProvider = mdns.NewAvahiProvider(m, ifaceIndexes) + if !m.mdnsProvider.CheckAvailability() { + m.mdnsProvider.Shutdown() + + // Avahi is not availble, use Zeroconf + m.mdnsProvider = mdns.NewZeroconfProvider(m, ifaces) + if !m.mdnsProvider.CheckAvailability() { + return errors.New("No mDNS provider available") + } + } + + // on startup always start mDNS announcement + if err := m.AnnounceMdnsEntry(); err != nil { + return err + } + + // catch signals + go func() { + signalC := make(chan os.Signal, 1) + signal.Notify(signalC, os.Interrupt, syscall.SIGTERM) + + <-signalC // wait for signal + + m.ShutdownMdnsService() + }() + + return nil +} + // Announces the service to the network via mDNS // A CEM service should always invoke this on startup // Any other service should only invoke this whenever it is not connected to a CEM service -func (m *mdns) AnnounceMdnsEntry() error { +func (m *mdnsManager) AnnounceMdnsEntry() error { if m.isAnnounced { return nil } - ifaces, ifaceIndexes, err := m.interfaces() - if err != nil { - return err - } - serviceIdentifier := m.configuration.Identifier() txt := []string{ // SHIP 7.3.2 @@ -196,85 +165,39 @@ func (m *mdns) AnnounceMdnsEntry() error { serviceName := m.configuration.MdnsServiceName() - if m.av == nil { - logging.Log.Debug("mdns: using zeroconf") - // use Zeroconf library if avahi is not available - // Set TTL to 2 minutes as defined in SHIP chapter 7 - mDNSServer, err := zeroconf.Register(serviceName, shipZeroConfServiceType, shipZeroConfDomain, m.configuration.port, txt, ifaces, zeroconf.TTL(120)) - if err == nil { - m.zc = mDNSServer - - m.isAnnounced = true - return nil - } - - return err - } - - // avahi - logging.Log.Debug("mdns: using avahi") - - entryGroup, err := m.av.EntryGroupNew() - if err != nil { - return err - } - - var btxt [][]byte - for _, t := range txt { - btxt = append(btxt, []byte(t)) - } - - for _, iface := range ifaceIndexes { - err = entryGroup.AddService(iface, avahi.ProtoUnspec, 0, serviceName, shipZeroConfServiceType, shipZeroConfDomain, "", uint16(m.configuration.port), btxt) - if err != nil { - return err - } - } - - err = entryGroup.Commit() - if err != nil { + if err := m.mdnsProvider.Announce(serviceName, m.configuration.port, txt); err != nil { + logging.Log.Debug("mdns: failure announcing service", err) return err } - m.avEntryGroup = entryGroup m.isAnnounced = true return nil } // Stop the mDNS announcement on the network -func (m *mdns) UnannounceMdnsEntry() { +func (m *mdnsManager) UnannounceMdnsEntry() { if !m.isAnnounced { return } - if m.zc != nil { - m.zc.Shutdown() - m.zc = nil - } - if m.av != nil { - m.av.EntryGroupFree(m.avEntryGroup) - m.avEntryGroup = nil - } + m.mdnsProvider.Unannounce() logging.Log.Debug("mdns: stop announcement") m.isAnnounced = false } // Shutdown all of mDNS -func (m *mdns) ShutdownMdnsService() { +func (m *mdnsManager) ShutdownMdnsService() { m.UnannounceMdnsEntry() - - if m.av != nil { - m.av.Close() - m.av = nil - } - m.stopResolvingEntries() + + m.mdnsProvider.Shutdown() + m.mdnsProvider = nil } // Register a callback to be invoked for found mDNS entries -func (m *mdns) RegisterMdnsSearch(cb MdnsSearch) { +func (m *mdnsManager) RegisterMdnsSearch(cb MdnsSearch) { if m.searchDelegate != cb { m.searchDelegate = cb } @@ -303,7 +226,7 @@ func (m *mdns) RegisterMdnsSearch(cb MdnsSearch) { } // Remove a callback for found mDNS entries and stop searching if no callbacks are left -func (m *mdns) UnregisterMdnsSearch(cb MdnsSearch) { +func (m *mdnsManager) UnregisterMdnsSearch(cb MdnsSearch) { m.mux.Lock() defer m.mux.Unlock() @@ -312,96 +235,10 @@ func (m *mdns) UnregisterMdnsSearch(cb MdnsSearch) { m.stopResolvingEntries() } -func (m *mdns) resolveEntriesAvahi() { - var err error - var end bool - - var avBrowser *avahi.ServiceBrowser - - // instead of limiting search on specific allowed interfaces, we allow all and filter the results - if avBrowser, err = m.av.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0); err != nil { - logging.Log.Debug("mdns: error setting up avahi browser:", err) - return - } - - if avBrowser == nil { - logging.Log.Debug("mdns: avahi browser is not available") - return - } - - for !end { - select { - case <-m.cancelChan: - end = true - break - case service := <-avBrowser.AddChannel: - m.processAvahiService(service, false) - case service := <-avBrowser.RemoveChannel: - m.processAvahiService(service, true) - } - } - - m.av.ServiceBrowserFree(avBrowser) -} - -func (m *mdns) resolveEntriesZeroconf() { - var end bool - - zcEntries := make(chan *zeroconf.ServiceEntry) - zcRemoved := make(chan *zeroconf.ServiceEntry) - defer close(zcEntries) - defer close(zcRemoved) - - // for Zeroconf we need a context - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - go func() { - _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries, zcRemoved) - }() - - for !end { - select { - case <-ctx.Done(): - end = true - break - case <-m.cancelChan: - ctx.Done() - case service := <-zcRemoved: - // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records - if len(service.Text) == 0 { - continue - } - - elements := m.parseTxt(service.Text) - - addresses := service.AddrIPv4 - m.processMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, true) - - case service := <-zcEntries: - // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records - if len(service.Text) == 0 { - continue - } - - elements := m.parseTxt(service.Text) - - addresses := service.AddrIPv4 - // Only use IPv4 for now - // addresses = append(addresses, service.AddrIPv6...) - m.processMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, false) - } - } -} - // search for mDNS entries and report them -func (m *mdns) resolveEntries() { +func (m *mdnsManager) resolveEntries() { go func() { - if m.av != nil { - m.resolveEntriesAvahi() - } else { - m.resolveEntriesZeroconf() - } + m.mdnsProvider.ResolveEntries(m.cancelChan) m.mux.Lock() m.isSearchingServices = false @@ -410,91 +247,24 @@ func (m *mdns) resolveEntries() { } // stop searching for mDNS entries -func (m *mdns) stopResolvingEntries() { - if m.cancelChan != nil { - if util.IsChannelClosed(m.cancelChan) { - return - } - - logging.Log.Debug("mdns: stop search") - - m.cancelChan <- true - } -} - -// process an avahi mDNS service -// as avahi returns a service per interface, we need to combine them -func (m *mdns) processAvahiService(service avahi.Service, remove bool) { - _, ifaceIndexes, err := m.interfaces() - if err != nil { - logging.Log.Debug("avahi - error getting interfaces:", err) - return - } - - // check if the service is within the allowed list - allow := false - if len(ifaceIndexes) == 1 && ifaceIndexes[0] == avahi.InterfaceUnspec { - allow = true - } else { - for _, iface := range ifaceIndexes { - if service.Interface == iface { - allow = true - break - } - } - } - - if !allow { - logging.Log.Debug("avahi - ignoring service as its interface is not in the allowed list:", service.Name) +func (m *mdnsManager) stopResolvingEntries() { + if m.cancelChan == nil { return } - resolved, err := m.av.ResolveService(service.Interface, service.Protocol, service.Name, service.Type, service.Domain, avahi.ProtoUnspec, 0) - if err != nil { - logging.Log.Debug("avahi - error resolving service:", err) + if util.IsChannelClosed(m.cancelChan) { return } - // convert [][]byte to []string manually - var txt []string - for _, element := range resolved.Txt { - txt = append(txt, string(element)) - } - elements := m.parseTxt(txt) + logging.Log.Debug("mdns: stop search") - // convert address to net.IP - address := net.ParseIP(resolved.Address) - // if the address can not be used, ignore the entry - if address == nil || address.IsUnspecified() { - logging.Log.Debug("avahi - service provides unusable address:", service.Name) - return - } - - // Ignore IPv6 addresses for now - if address.To4() == nil { - return - } - - m.processMdnsEntry(elements, resolved.Name, resolved.Host, []net.IP{address}, int(resolved.Port), remove) + m.cancelChan <- true } -// parse mDNS text fields -func (m *mdns) parseTxt(txt []string) map[string]string { - result := make(map[string]string) - - for _, item := range txt { - s := strings.Split(item, "=") - if len(s) != 2 { - continue - } - result[s[0]] = s[1] - } - - return result -} +// MdnsManager interface // process an mDNS entry and manage mDNS entries map -func (m *mdns) processMdnsEntry(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool) { +func (m *mdnsManager) ProcessMdnsEntry(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool) { // check for mandatory text elements mapItems := []string{"txtvers", "id", "path", "ski", "register"} for _, item := range mapItems { diff --git a/service/mdns/avahi.go b/service/mdns/avahi.go new file mode 100644 index 00000000..e64b9136 --- /dev/null +++ b/service/mdns/avahi.go @@ -0,0 +1,186 @@ +package mdns + +import ( + "net" + + "github.com/enbility/eebus-go/logging" + "github.com/godbus/dbus/v5" + "github.com/holoplot/go-avahi" +) + +type AvahiProvider struct { + mdnsManager MdnsManager + + ifaceIndexes []int32 + + avServer *avahi.Server + avEntryGroup *avahi.EntryGroup +} + +func NewAvahiProvider(mdnsManager MdnsManager, ifaceIndexes []int32) *AvahiProvider { + return &AvahiProvider{ + mdnsManager: mdnsManager, + ifaceIndexes: ifaceIndexes, + } +} + +var _ MdnsProvider = (*AvahiProvider)(nil) + +func (a *AvahiProvider) CheckAvailability() bool { + dbusConn, err := dbus.SystemBus() + if err != nil { + return false + } + + a.avServer, err = avahi.ServerNew(dbusConn) + if err != nil { + return false + } + + if _, err := a.avServer.GetAPIVersion(); err != nil { + return false + } + + avBrowser, err := a.avServer.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0) + if err != nil { + return false + } + + if avBrowser != nil { + a.avServer.ServiceBrowserFree(avBrowser) + return true + } + + return false +} + +func (a *AvahiProvider) Shutdown() { + if a.avServer == nil { + return + } + + a.avServer.Close() + a.avServer = nil + a.avEntryGroup = nil +} + +func (a *AvahiProvider) Announce(serviceName string, port int, txt []string) error { + logging.Log.Debug("mdns: using avahi") + + entryGroup, err := a.avServer.EntryGroupNew() + if err != nil { + return err + } + + var btxt [][]byte + for _, t := range txt { + btxt = append(btxt, []byte(t)) + } + + for _, iface := range a.ifaceIndexes { + err = entryGroup.AddService(iface, avahi.ProtoUnspec, 0, serviceName, shipZeroConfServiceType, shipZeroConfDomain, "", uint16(port), btxt) + if err != nil { + return err + } + } + + err = entryGroup.Commit() + if err != nil { + return err + } + + a.avEntryGroup = entryGroup + + return nil +} + +func (a *AvahiProvider) Unannounce() { + if a.avEntryGroup == nil { + return + } + + a.avServer.EntryGroupFree(a.avEntryGroup) + a.avEntryGroup = nil +} + +func (a *AvahiProvider) ResolveEntries(cancelChan chan bool) { + var err error + var end bool + + var avBrowser *avahi.ServiceBrowser + + // instead of limiting search on specific allowed interfaces, we allow all and filter the results + if avBrowser, err = a.avServer.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0); err != nil { + logging.Log.Debug("mdns: error setting up avahi browser:", err) + return + } + + if avBrowser == nil { + logging.Log.Debug("mdns: avahi browser is not available") + return + } + + for !end { + select { + case <-cancelChan: + end = true + break + case service := <-avBrowser.AddChannel: + a.processService(service, false) + case service := <-avBrowser.RemoveChannel: + a.processService(service, true) + } + } + + a.avServer.ServiceBrowserFree(avBrowser) +} + +// process an avahi mDNS service +// as avahi returns a service per interface, we need to combine them +func (a *AvahiProvider) processService(service avahi.Service, remove bool) { + // check if the service is within the allowed list + allow := false + if len(a.ifaceIndexes) == 1 && a.ifaceIndexes[0] == avahi.InterfaceUnspec { + allow = true + } else { + for _, iface := range a.ifaceIndexes { + if service.Interface == iface { + allow = true + break + } + } + } + + if !allow { + logging.Log.Debug("avahi - ignoring service as its interface is not in the allowed list:", service.Name) + return + } + + resolved, err := a.avServer.ResolveService(service.Interface, service.Protocol, service.Name, service.Type, service.Domain, avahi.ProtoUnspec, 0) + if err != nil { + logging.Log.Debug("avahi - error resolving service:", err) + return + } + + // convert [][]byte to []string manually + var txt []string + for _, element := range resolved.Txt { + txt = append(txt, string(element)) + } + elements := parseTxt(txt) + + // convert address to net.IP + address := net.ParseIP(resolved.Address) + // if the address can not be used, ignore the entry + if address == nil || address.IsUnspecified() { + logging.Log.Debug("avahi - service provides unusable address:", service.Name) + return + } + + // Ignore IPv6 addresses for now + if address.To4() == nil { + return + } + + a.mdnsManager.ProcessMdnsEntry(elements, resolved.Name, resolved.Host, []net.IP{address}, int(resolved.Port), remove) +} diff --git a/service/mdns/helper.go b/service/mdns/helper.go new file mode 100644 index 00000000..c2744288 --- /dev/null +++ b/service/mdns/helper.go @@ -0,0 +1,20 @@ +package mdns + +import ( + "strings" +) + +// parse mDNS text fields +func parseTxt(txt []string) map[string]string { + result := make(map[string]string) + + for _, item := range txt { + s := strings.Split(item, "=") + if len(s) != 2 { + continue + } + result[s[0]] = s[1] + } + + return result +} diff --git a/service/mdns/types.go b/service/mdns/types.go new file mode 100644 index 00000000..56ed6b2b --- /dev/null +++ b/service/mdns/types.go @@ -0,0 +1,18 @@ +package mdns + +import "net" + +const shipZeroConfServiceType = "_ship._tcp" +const shipZeroConfDomain = "local." + +type MdnsProvider interface { + CheckAvailability() bool + Shutdown() + Announce(serviceName string, port int, txt []string) error + Unannounce() + ResolveEntries(cancelChan chan bool) +} + +type MdnsManager interface { + ProcessMdnsEntry(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool) +} diff --git a/service/mdns/zeroconf.go b/service/mdns/zeroconf.go new file mode 100644 index 00000000..c7486dee --- /dev/null +++ b/service/mdns/zeroconf.go @@ -0,0 +1,106 @@ +package mdns + +import ( + "context" + "net" + + "github.com/DerAndereAndi/zeroconf/v2" + "github.com/enbility/eebus-go/logging" +) + +type ZeroconfProvider struct { + mdnsManager MdnsManager + + ifaces []net.Interface + + zc *zeroconf.Server +} + +func NewZeroconfProvider(mdnsManager MdnsManager, ifaces []net.Interface) *ZeroconfProvider { + return &ZeroconfProvider{ + mdnsManager: mdnsManager, + ifaces: ifaces, + } +} + +var _ MdnsProvider = (*ZeroconfProvider)(nil) + +func (z *ZeroconfProvider) CheckAvailability() bool { + return true +} + +func (z *ZeroconfProvider) Shutdown() {} + +func (z *ZeroconfProvider) Announce(serviceName string, port int, txt []string) error { + logging.Log.Debug("mdns: using zeroconf") + + // use Zeroconf library if avahi is not available + // Set TTL to 2 minutes as defined in SHIP chapter 7 + mDNSServer, err := zeroconf.Register(serviceName, shipZeroConfServiceType, shipZeroConfDomain, port, txt, z.ifaces, zeroconf.TTL(120)) + if err != nil { + return err + } + + z.zc = mDNSServer + + return nil +} + +func (z *ZeroconfProvider) Unannounce() { + if z.zc == nil { + return + } + + z.zc.Shutdown() + z.zc = nil +} + +func (z *ZeroconfProvider) ResolveEntries(cancelChan chan bool) { + var end bool + + zcEntries := make(chan *zeroconf.ServiceEntry) + zcRemoved := make(chan *zeroconf.ServiceEntry) + defer close(zcEntries) + defer close(zcRemoved) + + // for Zeroconf we need a context + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go func() { + _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries, zcRemoved) + }() + + for !end { + select { + case <-ctx.Done(): + end = true + break + case <-cancelChan: + ctx.Done() + case service := <-zcRemoved: + // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records + if len(service.Text) == 0 { + continue + } + + elements := parseTxt(service.Text) + + addresses := service.AddrIPv4 + z.mdnsManager.ProcessMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, true) + + case service := <-zcEntries: + // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records + if len(service.Text) == 0 { + continue + } + + elements := parseTxt(service.Text) + + addresses := service.AddrIPv4 + // Only use IPv4 for now + // addresses = append(addresses, service.AddrIPv6...) + z.mdnsManager.ProcessMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, false) + } + } +} From 620365f960ec9f69831c65ca4b5ffbbb0a043e39 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 17 Jul 2023 15:41:54 +0200 Subject: [PATCH 048/240] Support registering SKIs before start --- service/hub.go | 2 ++ service/mdns.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/service/hub.go b/service/hub.go index 1784cfb0..b6b33417 100644 --- a/service/hub.go +++ b/service/hub.go @@ -127,6 +127,8 @@ func (h *connectionsHub) start() { if err != nil { logging.Log.Debug("error during mdns setup:", err) } + + h.checkRestartMdnsSearch() } var _ ship.ShipServiceDataProvider = (*connectionsHub)(nil) diff --git a/service/mdns.go b/service/mdns.go index 838b10d0..25bcd271 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -237,6 +237,10 @@ func (m *mdnsManager) UnregisterMdnsSearch(cb MdnsSearch) { // search for mDNS entries and report them func (m *mdnsManager) resolveEntries() { + if m.mdnsProvider == nil { + m.isSearchingServices = false + return + } go func() { m.mdnsProvider.ResolveEntries(m.cancelChan) From af9b0d9611dfe908dfbce676998102bc43b0a62b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 18 Jul 2023 08:21:06 +0200 Subject: [PATCH 049/240] Use callback instead of interface --- service/mdns.go | 10 ++++------ service/mdns/avahi.go | 15 ++++++--------- service/mdns/types.go | 6 +----- service/mdns/zeroconf.go | 13 +++++-------- 4 files changed, 16 insertions(+), 28 deletions(-) diff --git a/service/mdns.go b/service/mdns.go index 25bcd271..a74f7c0c 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -111,12 +111,12 @@ func (m *mdnsManager) SetupMdnsService() error { return err } - m.mdnsProvider = mdns.NewAvahiProvider(m, ifaceIndexes) + m.mdnsProvider = mdns.NewAvahiProvider(ifaceIndexes) if !m.mdnsProvider.CheckAvailability() { m.mdnsProvider.Shutdown() // Avahi is not availble, use Zeroconf - m.mdnsProvider = mdns.NewZeroconfProvider(m, ifaces) + m.mdnsProvider = mdns.NewZeroconfProvider(ifaces) if !m.mdnsProvider.CheckAvailability() { return errors.New("No mDNS provider available") } @@ -242,7 +242,7 @@ func (m *mdnsManager) resolveEntries() { return } go func() { - m.mdnsProvider.ResolveEntries(m.cancelChan) + m.mdnsProvider.ResolveEntries(m.cancelChan, m.processMdnsEntry) m.mux.Lock() m.isSearchingServices = false @@ -265,10 +265,8 @@ func (m *mdnsManager) stopResolvingEntries() { m.cancelChan <- true } -// MdnsManager interface - // process an mDNS entry and manage mDNS entries map -func (m *mdnsManager) ProcessMdnsEntry(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool) { +func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool) { // check for mandatory text elements mapItems := []string{"txtvers", "id", "path", "ski", "register"} for _, item := range mapItems { diff --git a/service/mdns/avahi.go b/service/mdns/avahi.go index e64b9136..27bba30a 100644 --- a/service/mdns/avahi.go +++ b/service/mdns/avahi.go @@ -9,17 +9,14 @@ import ( ) type AvahiProvider struct { - mdnsManager MdnsManager - ifaceIndexes []int32 avServer *avahi.Server avEntryGroup *avahi.EntryGroup } -func NewAvahiProvider(mdnsManager MdnsManager, ifaceIndexes []int32) *AvahiProvider { +func NewAvahiProvider(ifaceIndexes []int32) *AvahiProvider { return &AvahiProvider{ - mdnsManager: mdnsManager, ifaceIndexes: ifaceIndexes, } } @@ -103,7 +100,7 @@ func (a *AvahiProvider) Unannounce() { a.avEntryGroup = nil } -func (a *AvahiProvider) ResolveEntries(cancelChan chan bool) { +func (a *AvahiProvider) ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) { var err error var end bool @@ -126,9 +123,9 @@ func (a *AvahiProvider) ResolveEntries(cancelChan chan bool) { end = true break case service := <-avBrowser.AddChannel: - a.processService(service, false) + a.processService(service, false, callback) case service := <-avBrowser.RemoveChannel: - a.processService(service, true) + a.processService(service, true, callback) } } @@ -137,7 +134,7 @@ func (a *AvahiProvider) ResolveEntries(cancelChan chan bool) { // process an avahi mDNS service // as avahi returns a service per interface, we need to combine them -func (a *AvahiProvider) processService(service avahi.Service, remove bool) { +func (a *AvahiProvider) processService(service avahi.Service, remove bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) { // check if the service is within the allowed list allow := false if len(a.ifaceIndexes) == 1 && a.ifaceIndexes[0] == avahi.InterfaceUnspec { @@ -182,5 +179,5 @@ func (a *AvahiProvider) processService(service avahi.Service, remove bool) { return } - a.mdnsManager.ProcessMdnsEntry(elements, resolved.Name, resolved.Host, []net.IP{address}, int(resolved.Port), remove) + callback(elements, resolved.Name, resolved.Host, []net.IP{address}, int(resolved.Port), remove) } diff --git a/service/mdns/types.go b/service/mdns/types.go index 56ed6b2b..b2cbd86b 100644 --- a/service/mdns/types.go +++ b/service/mdns/types.go @@ -10,9 +10,5 @@ type MdnsProvider interface { Shutdown() Announce(serviceName string, port int, txt []string) error Unannounce() - ResolveEntries(cancelChan chan bool) -} - -type MdnsManager interface { - ProcessMdnsEntry(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool) + ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) } diff --git a/service/mdns/zeroconf.go b/service/mdns/zeroconf.go index c7486dee..aa56f6e0 100644 --- a/service/mdns/zeroconf.go +++ b/service/mdns/zeroconf.go @@ -9,17 +9,14 @@ import ( ) type ZeroconfProvider struct { - mdnsManager MdnsManager - ifaces []net.Interface zc *zeroconf.Server } -func NewZeroconfProvider(mdnsManager MdnsManager, ifaces []net.Interface) *ZeroconfProvider { +func NewZeroconfProvider(ifaces []net.Interface) *ZeroconfProvider { return &ZeroconfProvider{ - mdnsManager: mdnsManager, - ifaces: ifaces, + ifaces: ifaces, } } @@ -55,7 +52,7 @@ func (z *ZeroconfProvider) Unannounce() { z.zc = nil } -func (z *ZeroconfProvider) ResolveEntries(cancelChan chan bool) { +func (z *ZeroconfProvider) ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) { var end bool zcEntries := make(chan *zeroconf.ServiceEntry) @@ -87,7 +84,7 @@ func (z *ZeroconfProvider) ResolveEntries(cancelChan chan bool) { elements := parseTxt(service.Text) addresses := service.AddrIPv4 - z.mdnsManager.ProcessMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, true) + callback(elements, service.Instance, service.HostName, addresses, service.Port, true) case service := <-zcEntries: // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records @@ -100,7 +97,7 @@ func (z *ZeroconfProvider) ResolveEntries(cancelChan chan bool) { addresses := service.AddrIPv4 // Only use IPv4 for now // addresses = append(addresses, service.AddrIPv6...) - z.mdnsManager.ProcessMdnsEntry(elements, service.Instance, service.HostName, addresses, service.Port, false) + callback(elements, service.Instance, service.HostName, addresses, service.Port, false) } } } From 8afee389a33032aa3caec2f85a6b86908be119d2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 18 Jul 2023 08:32:18 +0200 Subject: [PATCH 050/240] Remove log statement --- service/hub.go | 1 - 1 file changed, 1 deletion(-) diff --git a/service/hub.go b/service/hub.go index b6b33417..a4ba7584 100644 --- a/service/hub.go +++ b/service/hub.go @@ -196,7 +196,6 @@ func (h *connectionsHub) checkRestartMdnsSearch() { _ = h.mdns.AnnounceMdnsEntry() } - logging.Log.Debug("restarting mdns search") h.mdns.RegisterMdnsSearch(h) } } From aad1b2c5d1714dc315a64371aca939b9dd1779db Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 18 Jul 2023 08:41:58 +0200 Subject: [PATCH 051/240] Some cleanup --- service/mdns.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/service/mdns.go b/service/mdns.go index a74f7c0c..f30b6b6a 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -196,24 +196,27 @@ func (m *mdnsManager) ShutdownMdnsService() { m.mdnsProvider = nil } +func (m *mdnsManager) setIsSearchingServices(enable bool) { + m.mux.Lock() + defer m.mux.Unlock() + + m.isSearchingServices = enable +} + // Register a callback to be invoked for found mDNS entries func (m *mdnsManager) RegisterMdnsSearch(cb MdnsSearch) { + m.mux.Lock() if m.searchDelegate != cb { m.searchDelegate = cb } - - m.mux.Lock() + m.mux.Unlock() if !m.isSearchingServices { - m.isSearchingServices = true - m.mux.Unlock() - logging.Log.Debug("mdns: start search") + m.setIsSearchingServices(true) m.resolveEntries() return } - defer m.mux.Unlock() - // do we already know some entries? if len(m.entries) == 0 { return @@ -238,15 +241,14 @@ func (m *mdnsManager) UnregisterMdnsSearch(cb MdnsSearch) { // search for mDNS entries and report them func (m *mdnsManager) resolveEntries() { if m.mdnsProvider == nil { - m.isSearchingServices = false + m.setIsSearchingServices(false) return } go func() { + logging.Log.Debug("mdns: start search") m.mdnsProvider.ResolveEntries(m.cancelChan, m.processMdnsEntry) - m.mux.Lock() - m.isSearchingServices = false - m.mux.Unlock() + m.setIsSearchingServices(false) }() } From 291b3966f646135bcd1b84db8013b10635bf6b12 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 25 Jul 2023 19:43:37 +0200 Subject: [PATCH 052/240] Fix a possible panic --- service/mdns.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/service/mdns.go b/service/mdns.go index f30b6b6a..0fa2684b 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -370,7 +370,10 @@ func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host st } if m.searchDelegate != nil && updated { - mdnsEntries := m.entries + mdnsEntries := make(map[string]MdnsEntry) + for k, v := range m.entries { + mdnsEntries[k] = v + } go m.searchDelegate.ReportMdnsEntries(mdnsEntries) } } From 462aa4cbe62466f3ba28a1549c813529321d7a50 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 2 Sep 2023 17:17:08 +0200 Subject: [PATCH 053/240] Another Elli specific test --- features/electricalconnection_test.go | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 5bbd2280..a0d88d84 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -201,6 +201,19 @@ func (s *ElectricalConnectionSuite) Test_GetPermittedValueSets() { assert.NotNil(s.T(), data) } +func (s *ElectricalConnectionSuite) Test_GetPermittedValueSetsEmptyElli() { + data, err := s.electricalConnection.GetPermittedValueSets() + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + s.addPermittedValueSetEmptyElli() + + data, err = s.electricalConnection.GetPermittedValueSets() + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) + +} + func (s *ElectricalConnectionSuite) Test_GetPermittedValueSetForParameterId() { parametertId := model.ElectricalConnectionParameterIdType(1) data, err := s.electricalConnection.GetPermittedValueSetForParameterId(parametertId) @@ -461,3 +474,25 @@ func (s *ElectricalConnectionSuite) addPermittedValueSet() { } rF.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, fData, nil, nil) } + +func (s *ElectricalConnectionSuite) addPermittedValueSetEmptyElli() { + rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + fData := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), + PermittedValueSet: []model.ScaledNumberSetType{}, + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)), + }, + }, + } + rF.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, fData, nil, nil) +} From 57503c8ee22fd37ea2cd3c6973e7c06034cd6505 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 5 Sep 2023 22:59:33 +0200 Subject: [PATCH 054/240] Add SPINE 1.3 data model changes --- spine/function_data_cmd.go | 30 ++++++ spine/model/bill.go | 2 + spine/model/commandframe.go | 16 ++++ spine/model/commondatatypes.go | 144 +++++++++++++++++++--------- spine/model/deviceconfiguration.go | 43 ++++++++- spine/model/devicediagnosis.go | 18 ++-- spine/model/electricalconnection.go | 64 +++++++++++++ spine/model/identification.go | 46 +++++++++ spine/model/loadcontrol.go | 5 +- spine/model/measurement.go | 32 +++++++ spine/model/setpoint.go | 6 ++ spine/model/timeseries.go | 10 +- spine/model/usecaseinformation.go | 18 ++-- 13 files changed, 364 insertions(+), 70 deletions(-) diff --git a/spine/function_data_cmd.go b/spine/function_data_cmd.go index d0ac25ba..afc4c84d 100644 --- a/spine/function_data_cmd.go +++ b/spine/function_data_cmd.go @@ -135,6 +135,8 @@ func addSelectorToFilter[T any](filter model.FilterType, function model.Function result.ElectricalConnectionPermittedValueSetListDataSelectors = castData[model.ElectricalConnectionPermittedValueSetListDataSelectorsType](data) case model.FunctionTypeElectricalConnectionStateListData: result.ElectricalConnectionStateListDataSelectors = castData[model.ElectricalConnectionStateListDataSelectorsType](data) + case model.FunctionTypeElectricalConnectionCharacteristicListData: + result.ElectricalConnectionCharacteristicListDataSelectors = castData[model.ElectricalConnectionCharacteristicListDataSelectorsType](data) case model.FunctionTypeHvacOperationModeDescriptionListData: result.HvacOperationModeDescriptionListDataSelectors = castData[model.HvacOperationModeDescriptionListDataSelectorsType](data) case model.FunctionTypeHvacOverrunDescriptionListData: @@ -179,6 +181,8 @@ func addSelectorToFilter[T any](filter model.FilterType, function model.Function result.MeasurementDescriptionListDataSelectors = castData[model.MeasurementDescriptionListDataSelectorsType](data) case model.FunctionTypeMeasurementListData: result.MeasurementListDataSelectors = castData[model.MeasurementListDataSelectorsType](data) + case model.FunctionTypeMeasurementSeriesListData: + result.MeasurementSeriesListDataSelectors = castData[model.MeasurementSeriesListDataSelectorsType](data) case model.FunctionTypeMeasurementThresholdRelationListData: result.MeasurementThresholdRelationListDataSelectors = castData[model.MeasurementThresholdRelationListDataSelectorsType](data) case model.FunctionTypeMessagingListData: @@ -233,6 +237,10 @@ func addSelectorToFilter[T any](filter model.FilterType, function model.Function result.PowerTimeSlotValueListDataSelectors = castData[model.PowerTimeSlotValueListDataSelectorsType](data) case model.FunctionTypeSensingListData: result.SensingListDataSelectors = castData[model.SensingListDataSelectorsType](data) + case model.FunctionTypeSessionIdentificationListData: + result.SessionIdentificationListDataSelectors = castData[model.SessionIdentificationListDataSelectorsType](data) + case model.FunctionTypeSessionMeasurementRelationListData: + result.SessionMeasurementRelationListDataSelectors = castData[model.SessionMeasurementRelationListDataSelectorsType](data) case model.FunctionTypeSetpointConstraintsListData: result.SetpointConstraintsListDataSelectors = castData[model.SetpointConstraintsListDataSelectorsType](data) case model.FunctionTypeSetpointDescriptionListData: @@ -245,6 +253,8 @@ func addSelectorToFilter[T any](filter model.FilterType, function model.Function result.SmartEnergyManagementPsPriceDataSelectors = castData[model.SmartEnergyManagementPsPriceDataSelectorsType](data) case model.FunctionTypeSpecificationVersionListData: result.SpecificationVersionListDataSelectors = castData[model.SpecificationVersionListDataSelectorsType](data) + case model.FunctionTypeStateInformationListData: + result.StateInformationListDataSelectors = castData[model.StateInformationListDataSelectorsType](data) case model.FunctionTypeSupplyConditionListData: result.SupplyConditionListDataSelectors = castData[model.SupplyConditionListDataSelectorsType](data) case model.FunctionTypeSupplyConditionThresholdRelationListData: @@ -356,6 +366,8 @@ func addElementToFilter[T any](filter model.FilterType, function model.FunctionT result.ElectricalConnectionPermittedValueSetDataElements = castData[model.ElectricalConnectionPermittedValueSetDataElementsType](data) case model.FunctionTypeElectricalConnectionStateListData: result.ElectricalConnectionStateDataElements = castData[model.ElectricalConnectionStateDataElementsType](data) + case model.FunctionTypeElectricalConnectionCharacteristicListData: + result.ElectricalConnectionCharacteristicDataElements = castData[model.ElectricalConnectionCharacteristicDataElementsType](data) case model.FunctionTypeHvacOperationModeDescriptionListData: result.HvacOperationModeDescriptionDataElements = castData[model.HvacOperationModeDescriptionDataElementsType](data) case model.FunctionTypeHvacOverrunDescriptionListData: @@ -402,6 +414,8 @@ func addElementToFilter[T any](filter model.FilterType, function model.FunctionT result.MeasurementDescriptionDataElements = castData[model.MeasurementDescriptionDataElementsType](data) case model.FunctionTypeMeasurementListData: result.MeasurementDataElements = castData[model.MeasurementDataElementsType](data) + case model.FunctionTypeMeasurementSeriesListData: + result.MeasurementSeriesDataElements = castData[model.MeasurementSeriesDataElementsType](data) case model.FunctionTypeMeasurementThresholdRelationListData: result.MeasurementThresholdRelationDataElements = castData[model.MeasurementThresholdRelationDataElementsType](data) case model.FunctionTypeMessagingListData: @@ -488,6 +502,10 @@ func addElementToFilter[T any](filter model.FilterType, function model.FunctionT result.PowerTimeSlotValueDataElements = castData[model.PowerTimeSlotValueDataElementsType](data) case model.FunctionTypeSensingListData: result.SensingDataElements = castData[model.SensingDataElementsType](data) + case model.FunctionTypeSessionIdentificationListData: + result.SessionIdentificationDataElements = castData[model.SessionIdentificationDataElementsType](data) + case model.FunctionTypeSessionMeasurementRelationListData: + result.SessionMeasurementRelationDataElements = castData[model.SessionMeasurementRelationDataElementsType](data) case model.FunctionTypeSetpointConstraintsListData: result.SetpointConstraintsDataElements = castData[model.SetpointConstraintsDataElementsType](data) case model.FunctionTypeSetpointDescriptionListData: @@ -504,6 +522,8 @@ func addElementToFilter[T any](filter model.FilterType, function model.FunctionT result.SmartEnergyManagementPsPriceDataElements = castData[model.SmartEnergyManagementPsPriceDataElementsType](data) case model.FunctionTypeSpecificationVersionListData: result.SpecificationVersionDataElements = castData[model.SpecificationVersionDataElementsType](data) + case model.FunctionTypeStateInformationListData: + result.StateInformationDataElements = castData[model.StateInformationDataElementsType](data) case model.FunctionTypeSubscriptionManagementDeleteCall: result.SubscriptionManagementDeleteCallElements = castData[model.SubscriptionManagementDeleteCallElementsType](data) case model.FunctionTypeSubscriptionManagementEntryListData: @@ -635,6 +655,8 @@ func createCmd[T any](function model.FunctionType, data *T) model.CmdType { result.ElectricalConnectionPermittedValueSetListData = castData[model.ElectricalConnectionPermittedValueSetListDataType](data) case model.FunctionTypeElectricalConnectionStateListData: result.ElectricalConnectionStateListData = castData[model.ElectricalConnectionStateListDataType](data) + case model.FunctionTypeElectricalConnectionCharacteristicListData: + result.ElectricalConnectionCharacteristicListData = castData[model.ElectricalConnectionCharacteristicListDataType](data) case model.FunctionTypeHvacOperationModeDescriptionListData: result.HvacOperationModeDescriptionListData = castData[model.HvacOperationModeDescriptionListDataType](data) case model.FunctionTypeHvacOverrunDescriptionListData: @@ -681,6 +703,8 @@ func createCmd[T any](function model.FunctionType, data *T) model.CmdType { result.MeasurementDescriptionListData = castData[model.MeasurementDescriptionListDataType](data) case model.FunctionTypeMeasurementListData: result.MeasurementListData = castData[model.MeasurementListDataType](data) + case model.FunctionTypeMeasurementSeriesListData: + result.MeasurementSeriesListData = castData[model.MeasurementSeriesListDataType](data) case model.FunctionTypeMeasurementThresholdRelationListData: result.MeasurementThresholdRelationListData = castData[model.MeasurementThresholdRelationListDataType](data) case model.FunctionTypeMessagingListData: @@ -753,6 +777,10 @@ func createCmd[T any](function model.FunctionType, data *T) model.CmdType { result.SensingDescriptionData = castData[model.SensingDescriptionDataType](data) case model.FunctionTypeSensingListData: result.SensingListData = castData[model.SensingListDataType](data) + case model.FunctionTypeSessionIdentificationListData: + result.SessionIdentificationListData = castData[model.SessionIdentificationListDataType](data) + case model.FunctionTypeSessionMeasurementRelationListData: + result.SessionMeasurementRelationListData = castData[model.SessionMeasurementRelationListDataType](data) case model.FunctionTypeSetpointConstraintsListData: result.SetpointConstraintsListData = castData[model.SetpointConstraintsListDataType](data) case model.FunctionTypeSetpointDescriptionListData: @@ -769,6 +797,8 @@ func createCmd[T any](function model.FunctionType, data *T) model.CmdType { result.SmartEnergyManagementPsPriceData = castData[model.SmartEnergyManagementPsPriceDataType](data) case model.FunctionTypeSpecificationVersionListData: result.SpecificationVersionListData = castData[model.SpecificationVersionListDataType](data) + case model.FunctionTypeStateInformationListData: + result.StateInformationListData = castData[model.StateInformationListDataType](data) case model.FunctionTypeSupplyConditionListData: result.SupplyConditionListData = castData[model.SupplyConditionListDataType](data) case model.FunctionTypeSupplyConditionThresholdRelationListData: diff --git a/spine/model/bill.go b/spine/model/bill.go index a0589fbf..1d876d41 100644 --- a/spine/model/bill.go +++ b/spine/model/bill.go @@ -137,6 +137,7 @@ type BillDescriptionDataType struct { BillWriteable *bool `json:"billWriteable,omitempty"` UpdateRequired *bool `json:"updateRequired,omitempty"` SupportedBillType []BillTypeType `json:"supportedBillType,omitempty"` + SessionId *SessionIdType `json:"sessionId,omitempty"` } type BillDescriptionDataElementsType struct { @@ -144,6 +145,7 @@ type BillDescriptionDataElementsType struct { BillWriteable *ElementTagType `json:"billWriteable,omitempty"` UpdateRequired *ElementTagType `json:"updateRequired,omitempty"` SupportedBillType *ElementTagType `json:"supportedBillType,omitempty"` + SessionId *ElementTagType `json:"sessionId,omitempty"` } type BillDescriptionListDataType struct { diff --git a/spine/model/commandframe.go b/spine/model/commandframe.go index fb9c63a2..537e2075 100644 --- a/spine/model/commandframe.go +++ b/spine/model/commandframe.go @@ -34,6 +34,7 @@ type FilterType struct { ElectricalConnectionParameterDescriptionListDataSelectors *ElectricalConnectionParameterDescriptionListDataSelectorsType `json:"electricalConnectionParameterDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionParameterDescriptionListData"` ElectricalConnectionPermittedValueSetListDataSelectors *ElectricalConnectionPermittedValueSetListDataSelectorsType `json:"electricalConnectionPermittedValueSetListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionPermittedValueSetListData"` ElectricalConnectionStateListDataSelectors *ElectricalConnectionStateListDataSelectorsType `json:"electricalConnectionStateListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionStateListData"` + ElectricalConnectionCharacteristicListDataSelectors *ElectricalConnectionCharacteristicListDataSelectorsType `json:"electricalConnectionCharacteristicListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionCharacteristicListData"` HvacOperationModeDescriptionListDataSelectors *HvacOperationModeDescriptionListDataSelectorsType `json:"hvacOperationModeDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacOperationModeDescriptionListData"` HvacOverrunDescriptionListDataSelectors *HvacOverrunDescriptionListDataSelectorsType `json:"hvacOverrunDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacOverrunDescriptionListData"` HvacOverrunListDataSelectors *HvacOverrunListDataSelectorsType `json:"hvacOverrunListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacOverrunListData"` @@ -56,6 +57,7 @@ type FilterType struct { MeasurementConstraintsListDataSelectors *MeasurementConstraintsListDataSelectorsType `json:"measurementConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:measurementConstraintsListData"` MeasurementDescriptionListDataSelectors *MeasurementDescriptionListDataSelectorsType `json:"measurementDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:measurementDescriptionListData"` MeasurementListDataSelectors *MeasurementListDataSelectorsType `json:"measurementListDataSelectors,omitempty" eebus:"typ:selector,fct:measurementListData"` + MeasurementSeriesListDataSelectors *MeasurementSeriesListDataSelectorsType `json:"measurementSeriesListDataSelectors,omitempty" eebus:"measurementSeriesListData"` MeasurementThresholdRelationListDataSelectors *MeasurementThresholdRelationListDataSelectorsType `json:"measurementThresholdRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:measurementThresholdRelationListData"` MessagingListDataSelectors *MessagingListDataSelectorsType `json:"messagingListDataSelectors,omitempty" eebus:"typ:selector,fct:messagingListData"` NetworkManagementDeviceDescriptionListDataSelectors *NetworkManagementDeviceDescriptionListDataSelectorsType `json:"networkManagementDeviceDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:networkManagementDeviceDescriptionListData"` @@ -83,12 +85,15 @@ type FilterType struct { PowerTimeSlotScheduleListDataSelectors *PowerTimeSlotScheduleListDataSelectorsType `json:"powerTimeSlotScheduleListDataSelectors,omitempty" eebus:"typ:selector,fct:powerTimeSlotScheduleListData"` PowerTimeSlotValueListDataSelectors *PowerTimeSlotValueListDataSelectorsType `json:"powerTimeSlotValueListDataSelectors,omitempty" eebus:"typ:selector,fct:powerTimeSlotValueListData"` SensingListDataSelectors *SensingListDataSelectorsType `json:"sensingListDataSelectors,omitempty" eebus:"typ:selector,fct:sensingListData"` + SessionIdentificationListDataSelectors *SessionIdentificationListDataSelectorsType `json:"sessionIdentificationListDataSelectors,omitempty" eebus:"typ:selector,fct:sessionIdentificationListData"` + SessionMeasurementRelationListDataSelectors *SessionMeasurementRelationListDataSelectorsType `json:"sessionMeasurementRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:sessionMeasurementRelationListData"` SetpointConstraintsListDataSelectors *SetpointConstraintsListDataSelectorsType `json:"setpointConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:setpointConstraintsListData"` SetpointDescriptionListDataSelectors *SetpointDescriptionListDataSelectorsType `json:"setpointDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:setpointDescriptionListData"` SetpointListDataSelectors *SetpointListDataSelectorsType `json:"setpointListDataSelectors,omitempty" eebus:"typ:selector,fct:setpointListData"` SmartEnergyManagementPsDataSelectors *SmartEnergyManagementPsDataSelectorsType `json:"smartEnergyManagementPsDataSelectors,omitempty" eebus:"typ:selector,fct:smartEnergyManagementPsData"` SmartEnergyManagementPsPriceDataSelectors *SmartEnergyManagementPsPriceDataSelectorsType `json:"smartEnergyManagementPsPriceDataSelectors,omitempty" eebus:"typ:selector,fct:smartEnergyManagementPsPriceData"` SpecificationVersionListDataSelectors *SpecificationVersionListDataSelectorsType `json:"specificationVersionListDataSelectors,omitempty" eebus:"typ:selector,fct:specificationVersionListData"` + StateInformationListDataSelectors *StateInformationListDataSelectorsType `json:"stateInformationListDataSelectors,omitempty" eebus:"typ:selector,fct:stateInformationListData"` SubscriptionManagementEntryListDataSelectors *SubscriptionManagementEntryListDataSelectorsType `json:"subscriptionManagementEntryListDataSelectors,omitempty" eebus:"typ:selector,fct:subscriptionManagementEntryListData"` SupplyConditionDescriptionListDataSelectors *SupplyConditionDescriptionListDataSelectorsType `json:"supplyConditionDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:supplyConditionDescriptionListData"` SupplyConditionListDataSelectors *SupplyConditionListDataSelectorsType `json:"supplyConditionListDataSelectors,omitempty" eebus:"typ:selector,fct:supplyConditionListData"` @@ -144,6 +149,7 @@ type FilterType struct { ElectricalConnectionParameterDescriptionDataElements *ElectricalConnectionParameterDescriptionDataElementsType `json:"electricalConnectionParameterDescriptionDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionParameterDescriptionListData"` ElectricalConnectionPermittedValueSetDataElements *ElectricalConnectionPermittedValueSetDataElementsType `json:"electricalConnectionPermittedValueSetDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionPermittedValueSetListData"` ElectricalConnectionStateDataElements *ElectricalConnectionStateDataElementsType `json:"electricalConnectionStateDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionStateListData"` + ElectricalConnectionCharacteristicDataElements *ElectricalConnectionCharacteristicDataElementsType `json:"electricalConnectionCharacteristicDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionCharacteristicListData"` HvacOperationModeDescriptionDataElements *HvacOperationModeDescriptionDataElementsType `json:"hvacOperationModeDescriptionDataElements,omitempty" eebus:"typ:elements,fct:hvacOperationModeDescriptionListData"` HvacOverrunDataElements *HvacOverrunDataElementsType `json:"hvacOverrunDataElements,omitempty" eebus:"typ:elements,fct:hvacOverrunListData"` HvacOverrunDescriptionDataElements *HvacOverrunDescriptionDataElementsType `json:"hvacOverrunDescriptionDataElements,omitempty" eebus:"typ:elements,fct:hvacOverrunDescriptionListData"` @@ -167,6 +173,7 @@ type FilterType struct { MeasurementConstraintsDataElements *MeasurementConstraintsDataElementsType `json:"measurementConstraintsDataElements,omitempty" eebus:"typ:elements,fct:measurementConstraintsListData"` MeasurementDataElements *MeasurementDataElementsType `json:"measurementDataElements,omitempty" eebus:"typ:elements,fct:measurementListData"` MeasurementDescriptionDataElements *MeasurementDescriptionDataElementsType `json:"measurementDescriptionDataElements,omitempty" eebus:"typ:elements,fct:measurementDescriptionListData"` + MeasurementSeriesDataElements *MeasurementSeriesDataElementsType `json:"measurementSeriesDataElements,omitempty" eebus:"typ:elements,fct:measurementSeriesListData"` MeasurementThresholdRelationDataElements *MeasurementThresholdRelationDataElementsType `json:"measurementThresholdRelationDataElements,omitempty" eebus:"typ:elements,fct:measurementThresholdRelationListData"` MessagingDataElements *MessagingDataElementsType `json:"messagingDataElements,omitempty" eebus:"typ:elements,fct:messagingListData"` NetworkManagementAbortCallElements *NetworkManagementAbortCallElementsType `json:"networkManagementAbortCallElements,omitempty" eebus:"typ:elements,fct:networkManagementAbortCall"` @@ -211,6 +218,8 @@ type FilterType struct { PowerTimeSlotValueDataElements *PowerTimeSlotValueDataElementsType `json:"powerTimeSlotValueDataElements,omitempty" eebus:"typ:elements,fct:powerTimeSlotValueListData"` SensingDataElements *SensingDataElementsType `json:"sensingDataElements,omitempty" eebus:"typ:elements,fct:sensingListData"` SensingDescriptionDataElements *SensingDescriptionDataElementsType `json:"sensingDescriptionDataElements,omitempty" eebus:"typ:elements,fct:sensingDescriptionData"` + SessionIdentificationDataElements *SessionIdentificationDataElementsType `json:"sessionIdentificationDataElements,omitempty" eebus:"typ:elements,fct:sessionIdentificationData"` + SessionMeasurementRelationDataElements *SessionMeasurementRelationDataElementsType `json:"sessionMeasurementRelationDataElements,omitempty" eebus:"typ:elements,fct:sessionMeasurementRelationData"` SetpointConstraintsDataElements *SetpointConstraintsDataElementsType `json:"setpointConstraintsDataElements,omitempty" eebus:"typ:elements,fct:setpointConstraintsListData"` SetpointDataElements *SetpointDataElementsType `json:"setpointDataElements,omitempty" eebus:"typ:elements,fct:setpointListData"` SetpointDescriptionDataElements *SetpointDescriptionDataElementsType `json:"setpointDescriptionDataElements,omitempty" eebus:"typ:elements,fct:"` @@ -219,6 +228,7 @@ type FilterType struct { SmartEnergyManagementPsPriceCalculationRequestCallElements *SmartEnergyManagementPsPriceCalculationRequestCallElementsType `json:"smartEnergyManagementPsPriceCalculationRequestCallElements,omitempty" eebus:"typ:elements,fct:smartEnergyManagementPsPriceCalculationRequestCall"` SmartEnergyManagementPsPriceDataElements *SmartEnergyManagementPsPriceDataElementsType `json:"smartEnergyManagementPsPriceDataElements,omitempty" eebus:"typ:elements,fct:smartEnergyManagementPsPriceData"` SpecificationVersionDataElements *SpecificationVersionDataElementsType `json:"specificationVersionDataElements,omitempty" eebus:"typ:elements,fct:specificationVersionListData"` + StateInformationDataElements *StateInformationDataElementsType `json:"stateInformationDataElements,omitempty" eebus:"typ:elements,fct:stateInformationListData"` SubscriptionManagementDeleteCallElements *SubscriptionManagementDeleteCallElementsType `json:"subscriptionManagementDeleteCallElements,omitempty" eebus:"typ:elements,fct:subscriptionManagementDeleteCall"` SubscriptionManagementEntryDataElements *SubscriptionManagementEntryDataElementsType `json:"subscriptionManagementEntryDataElements,omitempty" eebus:"typ:elements,fct:subscriptionManagementEntryListData"` SubscriptionManagementRequestCallElements *SubscriptionManagementRequestCallElementsType `json:"subscriptionManagementRequestCallElements,omitempty" eebus:"typ:elements,fct:subscriptionManagementRequestCall"` @@ -293,6 +303,8 @@ type CmdType struct { ElectricalConnectionParameterDescriptionListData *ElectricalConnectionParameterDescriptionListDataType `json:"electricalConnectionParameterDescriptionListData,omitempty" eebus:"fct:electricalConnectionParameterDescriptionListData"` ElectricalConnectionPermittedValueSetListData *ElectricalConnectionPermittedValueSetListDataType `json:"electricalConnectionPermittedValueSetListData,omitempty" eebus:"fct:electricalConnectionPermittedValueSetListData"` ElectricalConnectionStateListData *ElectricalConnectionStateListDataType `json:"electricalConnectionStateListData,omitempty" eebus:"fct:electricalConnectionStateListData"` + ElectricalConnectionCharacteristicData *ElectricalConnectionCharacteristicDataType `json:"electricalConnectionCharacteristicData,omitempty" eebus:"fct:electricalConnectionCharacteristicData"` + ElectricalConnectionCharacteristicListData *ElectricalConnectionCharacteristicListDataType `json:"electricalConnectionCharacteristicListData,omitempty" eebus:"fct:electricalConnectionCharacteristicListData"` HvacOperationModeDescriptionListData *HvacOperationModeDescriptionListDataType `json:"hvacOperationModeDescriptionListData,omitempty" eebus:"fct:hvacOperationModeDescriptionListData"` HvacOverrunDescriptionListData *HvacOverrunDescriptionListDataType `json:"hvacOverrunDescriptionListData,omitempty" eebus:"fct:hvacOverrunDescriptionListData"` HvacOverrunListData *HvacOverrunListDataType `json:"hvacOverrunListData,omitempty" eebus:"fct:hvacOverrunListData"` @@ -316,6 +328,7 @@ type CmdType struct { MeasurementConstraintsListData *MeasurementConstraintsListDataType `json:"measurementConstraintsListData,omitempty" eebus:"fct:measurementConstraintsListData"` MeasurementDescriptionListData *MeasurementDescriptionListDataType `json:"measurementDescriptionListData,omitempty" eebus:"fct:measurementDescriptionListData"` MeasurementListData *MeasurementListDataType `json:"measurementListData,omitempty" eebus:"fct:measurementListData"` + MeasurementSeriesListData *MeasurementSeriesListDataType `json:"measurementSeriesListData,omitempty" eebus:"fct:measurementSeriesListData"` MeasurementThresholdRelationListData *MeasurementThresholdRelationListDataType `json:"measurementThresholdRelationListData,omitempty" eebus:"fct:measurementThresholdRelationListData"` MessagingListData *MessagingListDataType `json:"messagingListData,omitempty" eebus:"fct:messagingListData"` NetworkManagementAbortCall *NetworkManagementAbortCallType `json:"networkManagementAbortCall,omitempty" eebus:"fct:networkManagementAbortCall"` @@ -361,6 +374,8 @@ type CmdType struct { ResultData *ResultDataType `json:"resultData,omitempty" eebus:"fct:resultData"` SensingDescriptionData *SensingDescriptionDataType `json:"sensingDescriptionData,omitempty" eebus:"fct:sensingDescriptionData"` SensingListData *SensingListDataType `json:"sensingListData,omitempty" eebus:"fct:sensingListData"` + SessionIdentificationListData *SessionIdentificationListDataType `json:"sessionIdentificationListData,omitempty" eebus:"fct:sessionIdentificationListData"` + SessionMeasurementRelationListData *SessionMeasurementRelationListDataType `json:"sessionMeasurementRelationListData,omitempty" eebus:"fct:sessionMeasurementRelationListData"` SetpointConstraintsListData *SetpointConstraintsListDataType `json:"setpointConstraintsListData,omitempty" eebus:"fct:setpointConstraintsListData"` SetpointDescriptionListData *SetpointDescriptionListDataType `json:"setpointDescriptionListData,omitempty" eebus:"fct:setpointDescriptionListData"` SetpointListData *SetpointListDataType `json:"setpointListData,omitempty" eebus:"fct:setpointListData"` @@ -369,6 +384,7 @@ type CmdType struct { SmartEnergyManagementPsPriceCalculationRequestCall *SmartEnergyManagementPsPriceCalculationRequestCallType `json:"smartEnergyManagementPsPriceCalculationRequestCall,omitempty" eebus:"fct:smartEnergyManagementPsPriceCalculationRequestCall"` SmartEnergyManagementPsPriceData *SmartEnergyManagementPsPriceDataType `json:"smartEnergyManagementPsPriceData,omitempty" eebus:"fct:smartEnergyManagementPsPriceData"` SpecificationVersionListData *SpecificationVersionListDataType `json:"specificationVersionListData,omitempty" eebus:"fct:specificationVersionListData"` + StateInformationListData *StateInformationListDataType `json:"stateInformationListData,omitempty" eebus:"fct:stateInformationListData"` SubscriptionManagementDeleteCall *SubscriptionManagementDeleteCallType `json:"subscriptionManagementDeleteCall,omitempty" eebus:"fct:subscriptionManagementDeleteCall"` SubscriptionManagementEntryListData *SubscriptionManagementEntryListDataType `json:"subscriptionManagementEntryListData,omitempty" eebus:"fct:subscriptionManagementEntryListData"` SubscriptionManagementRequestCall *SubscriptionManagementRequestCallType `json:"subscriptionManagementRequestCall,omitempty" eebus:"fct:subscriptionManagementRequestCall"` diff --git a/spine/model/commondatatypes.go b/spine/model/commondatatypes.go index b77f32aa..a7606ae8 100644 --- a/spine/model/commondatatypes.go +++ b/spine/model/commondatatypes.go @@ -525,51 +525,93 @@ type FeatureAddressElementsType struct { type ScopeTypeType string const ( - ScopeTypeTypeAC ScopeTypeType = "ac" - ScopeTypeTypeACCosPhiGrid ScopeTypeType = "acCosPhiGrid" - ScopeTypeTypeACCurrentA ScopeTypeType = "acCurrentA" - ScopeTypeTypeACCurrentB ScopeTypeType = "acCurrentB" - ScopeTypeTypeACCurrentC ScopeTypeType = "acCurrentC" - ScopeTypeTypeACFrequency ScopeTypeType = "acFrequency" - ScopeTypeTypeACFrequencyGrid ScopeTypeType = "acFrequencyGrid" - ScopeTypeTypeACPowerA ScopeTypeType = "acPowerA" - ScopeTypeTypeACPowerB ScopeTypeType = "acPowerB" - ScopeTypeTypeACPowerC ScopeTypeType = "acPowerC" - ScopeTypeTypeACPowerLimitPct ScopeTypeType = "acPowerLimitPct" - ScopeTypeTypeACPowerTotal ScopeTypeType = "acPowerTotal" - ScopeTypeTypeACVoltageA ScopeTypeType = "acVoltageA" - ScopeTypeTypeACVoltageB ScopeTypeType = "acVoltageB" - ScopeTypeTypeACVoltageC ScopeTypeType = "acVoltageC" - ScopeTypeTypeACYieldDay ScopeTypeType = "acYieldDay" - ScopeTypeTypeACYieldTotal ScopeTypeType = "acYieldTotal" - ScopeTypeTypeDCCurrent ScopeTypeType = "dcCurrent" - ScopeTypeTypeDCPower ScopeTypeType = "dcPower" - ScopeTypeTypeDCString1 ScopeTypeType = "dcString1" - ScopeTypeTypeDCString2 ScopeTypeType = "dcString2" - ScopeTypeTypeDCString3 ScopeTypeType = "dcString3" - ScopeTypeTypeDCString4 ScopeTypeType = "dcString4" - ScopeTypeTypeDCString5 ScopeTypeType = "dcString5" - ScopeTypeTypeDCString6 ScopeTypeType = "dcString6" - ScopeTypeTypeDCTotal ScopeTypeType = "dcTotal" - ScopeTypeTypeDCVoltage ScopeTypeType = "dcVoltage" - ScopeTypeTypeDhwTemperature ScopeTypeType = "dhwTemperature" - ScopeTypeTypeFlowTemperature ScopeTypeType = "flowTemperature" - ScopeTypeTypeOutsideAirTemperature ScopeTypeType = "outsideAirTemperature" - ScopeTypeTypeReturnTemperature ScopeTypeType = "returnTemperature" - ScopeTypeTypeRoomAirTemperature ScopeTypeType = "roomAirTemperature" - ScopeTypeTypeCharge ScopeTypeType = "charge" - ScopeTypeTypeStateOfCharge ScopeTypeType = "stateOfCharge" - ScopeTypeTypeDischarge ScopeTypeType = "discharge" - ScopeTypeTypeGridConsumption ScopeTypeType = "gridConsumption" - ScopeTypeTypeGridFeedIn ScopeTypeType = "gridFeedIn" - ScopeTypeTypeSelfConsumption ScopeTypeType = "selfConsumption" - ScopeTypeTypeOverloadProtection ScopeTypeType = "overloadProtection" - ScopeTypeTypeACPower ScopeTypeType = "acPower" - ScopeTypeTypeACEnergy ScopeTypeType = "acEnergy" - ScopeTypeTypeACCurrent ScopeTypeType = "acCurrent" - ScopeTypeTypeACVoltage ScopeTypeType = "acVoltage" - ScopeTypeTypeBatteryControl ScopeTypeType = "batteryControl" - ScopeTypeTypeSimpleIncentiveTable ScopeTypeType = "simpleIncentiveTable" + ScopeTypeTypeAC ScopeTypeType = "ac" + ScopeTypeTypeACCosPhiGrid ScopeTypeType = "acCosPhiGrid" + ScopeTypeTypeACCurrentA ScopeTypeType = "acCurrentA" + ScopeTypeTypeACCurrentB ScopeTypeType = "acCurrentB" + ScopeTypeTypeACCurrentC ScopeTypeType = "acCurrentC" + ScopeTypeTypeACFrequency ScopeTypeType = "acFrequency" + ScopeTypeTypeACFrequencyGrid ScopeTypeType = "acFrequencyGrid" + ScopeTypeTypeACPowerA ScopeTypeType = "acPowerA" + ScopeTypeTypeACPowerB ScopeTypeType = "acPowerB" + ScopeTypeTypeACPowerC ScopeTypeType = "acPowerC" + ScopeTypeTypeACPowerLimitPct ScopeTypeType = "acPowerLimitPct" + ScopeTypeTypeACPowerTotal ScopeTypeType = "acPowerTotal" + ScopeTypeTypeACVoltageA ScopeTypeType = "acVoltageA" + ScopeTypeTypeACVoltageB ScopeTypeType = "acVoltageB" + ScopeTypeTypeACVoltageC ScopeTypeType = "acVoltageC" + ScopeTypeTypeACYieldDay ScopeTypeType = "acYieldDay" + ScopeTypeTypeACYieldTotal ScopeTypeType = "acYieldTotal" + ScopeTypeTypeDCCurrent ScopeTypeType = "dcCurrent" + ScopeTypeTypeDCPower ScopeTypeType = "dcPower" + ScopeTypeTypeDCString1 ScopeTypeType = "dcString1" + ScopeTypeTypeDCString2 ScopeTypeType = "dcString2" + ScopeTypeTypeDCString3 ScopeTypeType = "dcString3" + ScopeTypeTypeDCString4 ScopeTypeType = "dcString4" + ScopeTypeTypeDCString5 ScopeTypeType = "dcString5" + ScopeTypeTypeDCString6 ScopeTypeType = "dcString6" + ScopeTypeTypeDCTotal ScopeTypeType = "dcTotal" + ScopeTypeTypeDCVoltage ScopeTypeType = "dcVoltage" + ScopeTypeTypeDhwTemperature ScopeTypeType = "dhwTemperature" + ScopeTypeTypeFlowTemperature ScopeTypeType = "flowTemperature" + ScopeTypeTypeOutsideAirTemperature ScopeTypeType = "outsideAirTemperature" + ScopeTypeTypeReturnTemperature ScopeTypeType = "returnTemperature" + ScopeTypeTypeRoomAirTemperature ScopeTypeType = "roomAirTemperature" + ScopeTypeTypeCharge ScopeTypeType = "charge" + ScopeTypeTypeStateOfCharge ScopeTypeType = "stateOfCharge" + ScopeTypeTypeDischarge ScopeTypeType = "discharge" + ScopeTypeTypeGridConsumption ScopeTypeType = "gridConsumption" + ScopeTypeTypeGridFeedIn ScopeTypeType = "gridFeedIn" + ScopeTypeTypeSelfConsumption ScopeTypeType = "selfConsumption" + ScopeTypeTypeOverloadProtection ScopeTypeType = "overloadProtection" + ScopeTypeTypeACPower ScopeTypeType = "acPower" + ScopeTypeTypeACEnergy ScopeTypeType = "acEnergy" + ScopeTypeTypeACCurrent ScopeTypeType = "acCurrent" + ScopeTypeTypeACVoltage ScopeTypeType = "acVoltage" + ScopeTypeTypeBatteryControl ScopeTypeType = "batteryControl" + ScopeTypeTypeSimpleIncentiveTable ScopeTypeType = "simpleIncentiveTable" + ScopeTypeTypeStateOfHealth ScopeTypeType = "stateOfHealth" + ScopeTypeTypeTravelRange ScopeTypeType = "travelRange" + ScopeTypeTypeNominalEnergyCapacity ScopeTypeType = "nominalEnergyCapacity" + ScopeTypeTypeAcPowerReal ScopeTypeType = "acPowerReal" + ScopeTypeTypeAcPowerApparent ScopeTypeType = "acPowerApparent" + ScopeTypeTypeAcPowerReactive ScopeTypeType = "acPowerReactive" + ScopeTypeTypeAcYieldMonth ScopeTypeType = "acYieldMonth" + ScopeTypeTypeAcYieldYear ScopeTypeType = "acYieldYear" + ScopeTypeTypeAcFrequency ScopeTypeType = "acFrequency" + ScopeTypeTypeAcCosPhi ScopeTypeType = "acCosPhi" + ScopeTypeTypeDcEnergy ScopeTypeType = "dcEnergy" + ScopeTypeTypeInsulationResistance ScopeTypeType = "insulationResistance" + ScopeTypeTypeStateOfEnergy ScopeTypeType = "stateOfEnergy" + ScopeTypeTypeUseableCapacity ScopeTypeType = "useableCapacity" + ScopeTypeTypeDcChargeEnergy ScopeTypeType = "dcChargeEnergy" + ScopeTypeTypeDcDischargeEnergy ScopeTypeType = "dcDischargeEnergy" + ScopeTypeTypeLoadCycleCount ScopeTypeType = "loadCycleCount" + ScopeTypeTypeComponentTemperature ScopeTypeType = "componentTemperature" + ScopeTypeTypeGridLimit ScopeTypeType = "gridLimit" + ScopeTypeTypeGridLimitFallback ScopeTypeType = "gridLimitFallback" + ScopeTypeTypeAcPowerApparentTotal ScopeTypeType = "acPowerApparentTotal" + ScopeTypeTypeAcPowerReactiveTotal ScopeTypeType = "acPowerReactiveTotal" + ScopeTypeTypeAcCurrentTotal ScopeTypeType = "acCurrentTotal" + ScopeTypeTypeAcEnergyConsumed ScopeTypeType = "acEnergyConsumed" + ScopeTypeTypeAcEnergyProduced ScopeTypeType = "acEnergyProduced" + ScopeTypeTypeBatteryAcPower ScopeTypeType = "batteryAcPower" + ScopeTypeTypeBatteryAcPowerPhaseSpecific ScopeTypeType = "batteryAcPowerPhaseSpecific" + ScopeTypeTypeBatteryDcPower ScopeTypeType = "batteryDcPower" + ScopeTypeTypePccPower ScopeTypeType = "pccPower" + ScopeTypeTypeActivePowerLimit ScopeTypeType = "activePowerLimit" + ScopeTypeTypeActivePowerLimitPercentage ScopeTypeType = "activePowerLimitPercentage" + ScopeTypeTypeSimpleCommittedIncentiveTable ScopeTypeType = "simpleCommittedIncentiveTable" + ScopeTypeTypeSimplePreliminaryIncentiveTable ScopeTypeType = "simplePreliminaryIncentiveTable" + ScopeTypeTypeCommittedPowerPlan ScopeTypeType = "committedPowerPlan" + ScopeTypeTypePreliminaryPowerPlan ScopeTypeType = "preliminaryPowerPlan" + ScopeTypeTypeIncentiveTableEnConsWithPoETF ScopeTypeType = "incentiveTableEnConsWithPoETF" + ScopeTypeTypeIncentiveTableEnProdWithPoETF ScopeTypeType = "incentiveTableEnProdWithPoETF" + ScopeTypeTypeIncentiveTableEnConsWithPoE ScopeTypeType = "incentiveTableEnConsWithPoE" + ScopeTypeTypeIncentiveTableEnProdWithPoE ScopeTypeType = "incentiveTableEnProdWithPoE" + ScopeTypeTypeIncentiveTableEnConsWithTF ScopeTypeType = "incentiveTableEnConsWithTF" + ScopeTypeTypeIncentiveTableEnProdWithTF ScopeTypeType = "incentiveTableEnProdWithTF" + ScopeTypeTypeActivePowerForecast ScopeTypeType = "activePowerForecast" ) type RoleType string @@ -646,6 +688,12 @@ const ( EntityTypeTypeEVSE EntityTypeType = "EVSE" EntityTypeTypeChargingOutlet EntityTypeType = "ChargingOutlet" EntityTypeTypeCEM EntityTypeType = "CEM" + EntityTypeTypePV EntityTypeType = "PV" + EntityTypeTypePVESHybrid EntityTypeType = "PVESHybrid" + EntityTypeTypeElectricalStorage EntityTypeType = "ElectricalStorage" + EntityTypeTypePVString EntityTypeType = "PVString" + EntityTypeTypeGridGuard EntityTypeType = "GridGuard" + EntityTypeTypeControllableSystem EntityTypeType = "ControllableSystem" ) type FeatureTypeType string @@ -682,6 +730,7 @@ const ( FeatureTypeTypeIncentiveTable FeatureTypeType = "IncentiveTable" FeatureTypeTypeBill FeatureTypeType = "Bill" FeatureTypeTypeIdentification FeatureTypeType = "Identification" + FeatureTypeTypeStateInformation FeatureTypeType = "StateInformation" ) type FeatureSpecificUsageType string @@ -837,6 +886,8 @@ const ( FunctionTypeResultData FunctionType = "resultData" FunctionTypeSensingDescriptionData FunctionType = "sensingDescriptionData" FunctionTypeSensingListData FunctionType = "sensingListData" + FunctionTypeSessionIdentificationListData FunctionType = "sessionIdentificationListData" + FunctionTypeSessionMeasurementRelationListData FunctionType = "sessionMeasurementRelationListData" FunctionTypeSetpointConstraintsListData FunctionType = "setpointConstraintsListData" FunctionTypeSetpointDescriptionListData FunctionType = "setpointDescriptionListData" FunctionTypeSetpointListData FunctionType = "setpointListData" @@ -898,6 +949,9 @@ const ( FunctionTypeBillDescriptionListData FunctionType = "billDescriptionListData" FunctionTypeBillListData FunctionType = "billListData" FunctionTypeIdentificationListData FunctionType = "identificationListData" + FunctionTypeMeasurementSeriesListData FunctionType = "measurementSeriesListData" + FunctionTypeElectricalConnectionCharacteristicListData FunctionType = "electricalConnectionCharacteristicListData" + FunctionTypeStateInformationListData FunctionType = "stateInformationListData" ) type PossibleOperationsClassifierType struct { diff --git a/spine/model/deviceconfiguration.go b/spine/model/deviceconfiguration.go index a0f87144..967ea332 100644 --- a/spine/model/deviceconfiguration.go +++ b/spine/model/deviceconfiguration.go @@ -7,10 +7,43 @@ type DeviceConfigurationKeyValueStringType string type DeviceConfigurationKeyNameType string const ( - DeviceConfigurationKeyNameTypePeakPowerOfPVSystem DeviceConfigurationKeyNameType = "peakPowerOfPvSystem" - DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor DeviceConfigurationKeyNameType = "pvCurtailmentLimitFactor" - DeviceConfigurationKeyNameTypeAsymmetricChargingSupported DeviceConfigurationKeyNameType = "asymmetricChargingSupported" - DeviceConfigurationKeyNameTypeCommunicationsStandard DeviceConfigurationKeyNameType = "communicationsStandard" + DeviceConfigurationKeyNameTypePeakPowerOfPVSystem DeviceConfigurationKeyNameType = "peakPowerOfPvSystem" + DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor DeviceConfigurationKeyNameType = "pvCurtailmentLimitFactor" + DeviceConfigurationKeyNameTypeAsymmetricChargingSupported DeviceConfigurationKeyNameType = "asymmetricChargingSupported" + DeviceConfigurationKeyNameTypeCommunicationsStandard DeviceConfigurationKeyNameType = "communicationsStandard" + DeviceConfigurationKeyNameTypeInverterGridCode DeviceConfigurationKeyNameType = "inverterGridCode" + DeviceConfigurationKeyNameTypePvStringAvailabilityStatus DeviceConfigurationKeyNameType = "pvStringAvailabilityStatus" + DeviceConfigurationKeyNameTypeBatteryAvailabilityStatus DeviceConfigurationKeyNameType = "batteryAvailabilityStatus" + DeviceConfigurationKeyNameTypeGridConnectionStatus DeviceConfigurationKeyNameType = "gridConnectionStatus" + DeviceConfigurationKeyNameTypeTimeToAcChargePowerMax DeviceConfigurationKeyNameType = "timeToAcChargePowerMax" + DeviceConfigurationKeyNameTypeTimeToAcDischargePowerMax DeviceConfigurationKeyNameType = "timeToAcDischargePowerMax" + DeviceConfigurationKeyNameTypeTilt DeviceConfigurationKeyNameType = "tilt" + DeviceConfigurationKeyNameTypeAzimuth DeviceConfigurationKeyNameType = "azimuth" + DeviceConfigurationKeyNameTypeBatteryType DeviceConfigurationKeyNameType = "batteryType" + DeviceConfigurationKeyNameTypeMaxCycleCountPerDay DeviceConfigurationKeyNameType = "maxCycleCountPerDay" + DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit DeviceConfigurationKeyNameType = "failsafeConsumptionActivePowerLimit" + DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit DeviceConfigurationKeyNameType = "failsafeProductionActivePowerLimit" + DeviceConfigurationKeyNameTypeFailsafePositiveReactivePowerLimit DeviceConfigurationKeyNameType = "failsafePositiveReactivePowerLimit" + DeviceConfigurationKeyNameTypeFailsafeNegativeReactivePowerLimit DeviceConfigurationKeyNameType = "failsafeNegativeReactivePowerLimit" + DeviceConfigurationKeyNameTypeFailsafePositiveCosPhiLimit DeviceConfigurationKeyNameType = "failsafePositiveCosPhiLimit" + DeviceConfigurationKeyNameTypeFailsafeNegativeCosPhiLimit DeviceConfigurationKeyNameType = "failsafeNegativeCosPhiLimit" + DeviceConfigurationKeyNameTypeMaxAcChargePower DeviceConfigurationKeyNameType = "maxAcChargePower" + DeviceConfigurationKeyNameTypeMaxAcDischargePower DeviceConfigurationKeyNameType = "maxAcDischargePower" + DeviceConfigurationKeyNameTypeMaxDcChargePower DeviceConfigurationKeyNameType = "maxDcChargePower" + DeviceConfigurationKeyNameTypeMaxDcDischargePower DeviceConfigurationKeyNameType = "maxDcDischargePower" + DeviceConfigurationKeyNameTypeBatteryActiveControlMode DeviceConfigurationKeyNameType = "batteryActiveControlMode" + DeviceConfigurationKeyNameTypeDefaultAcPower DeviceConfigurationKeyNameType = "defaultAcPower" + DeviceConfigurationKeyNameTypeDefaultDcPower DeviceConfigurationKeyNameType = "defaultDcPower" + DeviceConfigurationKeyNameTypeDefaultPccPower DeviceConfigurationKeyNameType = "defaultPccPower" + DeviceConfigurationKeyNameTypeFailsafeAcPowerSetpoint DeviceConfigurationKeyNameType = "failsafeAcPowerSetpoint" + DeviceConfigurationKeyNameTypeFailsafeDcPowerSetpoint DeviceConfigurationKeyNameType = "failsafeDcPowerSetpoint" + DeviceConfigurationKeyNameTypeFailsafePccPowerSetpoint DeviceConfigurationKeyNameType = "failsafePccPowerSetpoint" + DeviceConfigurationKeyNameTypeFailsafeDurationMinimum DeviceConfigurationKeyNameType = "failsafeDurationMinimum" + DeviceConfigurationKeyNameTypeDischargingBelowTargetEnergyRequestPermitted DeviceConfigurationKeyNameType = "dischargingBelowTargetEnergyRequestPermitted" + DeviceConfigurationKeyNameTypeIncentivesSimulationCyclesMax DeviceConfigurationKeyNameType = "incentivesSimulationCyclesMax" + DeviceConfigurationKeyNameTypeIncentivesSimulationConcurrent DeviceConfigurationKeyNameType = "incentivesSimulationConcurrent" + DeviceConfigurationKeyNameTypeIncentivesTimeoutIncentiveRequest DeviceConfigurationKeyNameType = "incentivesTimeoutIncentiveRequest" + DeviceConfigurationKeyNameTypeIncentivesWaitIncentiveWriteable DeviceConfigurationKeyNameType = "incentivesWaitIncentiveWriteable" ) type DeviceConfigurationKeyValueTypeType string @@ -23,6 +56,7 @@ const ( DeviceConfigurationKeyValueTypeTypeString DeviceConfigurationKeyValueTypeType = "string" DeviceConfigurationKeyValueTypeTypeTime DeviceConfigurationKeyValueTypeType = "time" DeviceConfigurationKeyValueTypeTypeScaledNumber DeviceConfigurationKeyValueTypeType = "scaledNumber" + DeviceConfigurationKeyValueTypeTypeInteger DeviceConfigurationKeyValueTypeType = "integer" ) type DeviceConfigurationKeyValueValueType struct { @@ -33,6 +67,7 @@ type DeviceConfigurationKeyValueValueType struct { String *DeviceConfigurationKeyValueStringType `json:"string,omitempty"` Time *TimeType `json:"time,omitempty"` ScaledNumber *ScaledNumberType `json:"scaledNumber,omitempty"` + Integer *int64 `json:"integer,omitempty"` } type DeviceConfigurationKeyValueValueElementsType struct { diff --git a/spine/model/devicediagnosis.go b/spine/model/devicediagnosis.go index eb844fac..da5faba7 100644 --- a/spine/model/devicediagnosis.go +++ b/spine/model/devicediagnosis.go @@ -7,14 +7,16 @@ type LastErrorCodeType string type DeviceDiagnosisOperatingStateType string const ( - DeviceDiagnosisOperatingStateTypeNormalOperation DeviceDiagnosisOperatingStateType = "normalOperation" - DeviceDiagnosisOperatingStateTypeStandby DeviceDiagnosisOperatingStateType = "standby" - DeviceDiagnosisOperatingStateTypeFailure DeviceDiagnosisOperatingStateType = "failure" - DeviceDiagnosisOperatingStateTypeServiceNeeded DeviceDiagnosisOperatingStateType = "serviceNeeded" - DeviceDiagnosisOperatingStateTypeOverrideDetected DeviceDiagnosisOperatingStateType = "overrideDetected" - DeviceDiagnosisOperatingStateTypeInAlarm DeviceDiagnosisOperatingStateType = "inAlarm" - DeviceDiagnosisOperatingStateTypeNotReachable DeviceDiagnosisOperatingStateType = "notReachable" - DeviceDiagnosisOperatingStateTypeFinished DeviceDiagnosisOperatingStateType = "finished" + DeviceDiagnosisOperatingStateTypeNormalOperation DeviceDiagnosisOperatingStateType = "normalOperation" + DeviceDiagnosisOperatingStateTypeStandby DeviceDiagnosisOperatingStateType = "standby" + DeviceDiagnosisOperatingStateTypeFailure DeviceDiagnosisOperatingStateType = "failure" + DeviceDiagnosisOperatingStateTypeServiceNeeded DeviceDiagnosisOperatingStateType = "serviceNeeded" + DeviceDiagnosisOperatingStateTypeOverrideDetected DeviceDiagnosisOperatingStateType = "overrideDetected" + DeviceDiagnosisOperatingStateTypeInAlarm DeviceDiagnosisOperatingStateType = "inAlarm" + DeviceDiagnosisOperatingStateTypeNotReachable DeviceDiagnosisOperatingStateType = "notReachable" + DeviceDiagnosisOperatingStateTypeFinished DeviceDiagnosisOperatingStateType = "finished" + DeviceDiagnosisOperatingStateTypeTemporarilyNotReady DeviceDiagnosisOperatingStateType = "temporarilyNotReady" + DeviceDiagnosisOperatingStateTypeOff DeviceDiagnosisOperatingStateType = "off" ) type PowerSupplyConditionType string diff --git a/spine/model/electricalconnection.go b/spine/model/electricalconnection.go index a0bbbddb..95be03e9 100644 --- a/spine/model/electricalconnection.go +++ b/spine/model/electricalconnection.go @@ -4,6 +4,8 @@ type ElectricalConnectionIdType uint type ElectricalConnectionParameterIdType uint +type ElectricalConnectionCharaceteristicsIdType uint + type ElectricalConnectionMeasurandVariantType string const ( @@ -55,6 +57,36 @@ const ( ElectricalConnectionConnectionPointTypeOther ElectricalConnectionConnectionPointType = "other" ) +type ElectricalConnectionCharacteristicIdType uint + +type ElectricalConnectionCharacteristicContextType string + +const ( + ElectricalConnectionCharacteristicContextTypeDevice ElectricalConnectionCharacteristicContextType = "device" + ElectricalConnectionCharacteristicContextTypeEntity ElectricalConnectionCharacteristicContextType = "entity" + ElectricalConnectionCharacteristicContextTypeInverter ElectricalConnectionCharacteristicContextType = "inverter" + ElectricalConnectionCharacteristicContextTypePvString ElectricalConnectionCharacteristicContextType = "pvString" + ElectricalConnectionCharacteristicContextTypeBattery ElectricalConnectionCharacteristicContextType = "battery" +) + +type ElectricalConnectionCharacteristicTypeType string + +const ( + ElectricalConnectionCharacteristicTypeTypePowerConsumptionMin ElectricalConnectionCharacteristicTypeType = "powerConsumptionMin" + ElectricalConnectionCharacteristicTypeTypePowerConsumptionMax ElectricalConnectionCharacteristicTypeType = "powerConsumptionMax" + ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMin ElectricalConnectionCharacteristicTypeType = "powerConsumptionNominalMin" + ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax ElectricalConnectionCharacteristicTypeType = "powerConsumptionNominalMax" + ElectricalConnectionCharacteristicTypeTypePowerProductionMin ElectricalConnectionCharacteristicTypeType = "powerProductionMin" + ElectricalConnectionCharacteristicTypeTypePowerProductionMax ElectricalConnectionCharacteristicTypeType = "powerProductionMax" + ElectricalConnectionCharacteristicTypeTypePowerProductionNominalMin ElectricalConnectionCharacteristicTypeType = "powerProductionNominalMin" + ElectricalConnectionCharacteristicTypeTypePowerProductionNominalMax ElectricalConnectionCharacteristicTypeType = "powerProductionNominalMax" + ElectricalConnectionCharacteristicTypeTypeEnergyCapacityNominalMax ElectricalConnectionCharacteristicTypeType = "energyCapacityNominalMax" + ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax ElectricalConnectionCharacteristicTypeType = "contractualConsumptionNominalMax" + ElectricalConnectionCharacteristicTypeTypeContracutalProductionNominalMax ElectricalConnectionCharacteristicTypeType = "contractualProductionNominalMax" + ElectricalConnectionCharacteristicTypeTypeApparentPowerProductionNominalMax ElectricalConnectionCharacteristicTypeType = "apparentPowerProductionNominalMax" + ElectricalConnectionCharacteristicTypeTypeApparentPowerConsumptionNominalMax ElectricalConnectionCharacteristicTypeType = "apparentPowerConsumptionNominalMax" +) + type ElectricalConnectionParameterDescriptionDataType struct { ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty" eebus:"key"` ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty" eebus:"key"` @@ -175,3 +207,35 @@ type ElectricalConnectionDescriptionListDataSelectorsType struct { ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty"` ScopeType *ScopeTypeType `json:"scopeType,omitempty"` } + +type ElectricalConnectionCharacteristicDataType struct { + ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty" eebus:"key"` + ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty" eebus:"key"` + CharacteristicId *ElectricalConnectionCharaceteristicsIdType `json:"characteristicId,omitempty" eebus:"key"` + CharacteristicContext *ElectricalConnectionCharacteristicContextType `json:"characteristicContext,omitempty"` + CharacteristicType *ElectricalConnectionCharacteristicTypeType `json:"characteristicType,omitempty"` + Value *ScaledNumberType `json:"value,omitempty"` + Unit *UnitOfMeasurementType `json:"unit,omitempty"` +} + +type ElectricalConnectionCharacteristicDataElementsType struct { + ElectricalConnectionId *ElementTagType `json:"electricalConnectionId,omitempty"` + ParameterId *ElementTagType `json:"parameterId,omitempty"` + CharacteristicId *ElementTagType `json:"characteristicId,omitempty"` + CharacteristicContext *ElementTagType `json:"characteristicContext,omitempty"` + CharacteristicType *ElementTagType `json:"characteristicType,omitempty"` + Value *ScaledNumberElementsType `json:"value,omitempty"` + Unit *ElementTagType `json:"unit,omitempty"` +} + +type ElectricalConnectionCharacteristicListDataType struct { + ElectricalConnectionCharacteristicListData []ElectricalConnectionCharacteristicDataType `json:"electricalConnectionCharacteristicListData,omitempty"` +} + +type ElectricalConnectionCharacteristicListDataSelectorsType struct { + ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty"` + ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty"` + CharacteristicId *ElectricalConnectionCharaceteristicsIdType `json:"characteristicId,omitempty"` + CharacteristicContext *ElectricalConnectionCharacteristicContextType `json:"characteristicContext,omitempty"` + CharacteristicType *ElectricalConnectionCharacteristicTypeType `json:"characteristicType,omitempty"` +} diff --git a/spine/model/identification.go b/spine/model/identification.go index f42e3dc5..6e843293 100644 --- a/spine/model/identification.go +++ b/spine/model/identification.go @@ -12,6 +12,8 @@ const ( type IdentificationValueType string +type SessionIdType uint + type IdentificationDataType struct { IdentificationId *IdentificationIdType `json:"identificationId,omitempty" eebus:"key"` IdentificationType *IdentificationTypeType `json:"identificationType,omitempty"` @@ -34,3 +36,47 @@ type IdentificationListDataSelectorsType struct { IdentificationId *IdentificationIdType `json:"identificationId,omitempty"` IdentificationType *IdentificationTypeType `json:"identificationType,omitempty"` } + +type SessionIdentificationDataType struct { + SessionId *SessionIdType `json:"sessionId,omitempty" eebus:"key"` + IdentificationId *IdentificationIdType `json:"identificationId,omitempty"` + IsLatestSession *bool `json:"isLatestSession,omitempty"` + TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` +} + +type SessionIdentificationDataElementsType struct { + SessionId *ElementTagType `json:"sessionId,omitempty"` + IdentificationId *ElementTagType `json:"identificationId,omitempty"` + IsLatestSession *ElementTagType `json:"isLatestSession,omitempty"` + TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` +} + +type SessionIdentificationListDataType struct { + SessionIdentificationData []SessionIdentificationDataType `json:"sessionIdentificationData,omitempty"` +} + +type SessionIdentificationListDataSelectorsType struct { + SessionId *SessionIdType `json:"sessionId,omitempty"` + IdentificationId *IdentificationIdType `json:"identificationId,omitempty"` + IsLatestSession *bool `json:"isLatestSession,omitempty"` + TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` +} + +type SessionMeasurementRelationDataType struct { + SessionId *SessionIdType `json:"sessionId,omitempty" eebus:"key"` + MeasurementId []MeasurementIdType `json:"measurementId,omitempty"` +} + +type SessionMeasurementRelationDataElementsType struct { + SessionId *ElementTagType `json:"sessionId,omitempty"` + MeasurementId *ElementTagType `json:"measurementId,omitempty"` +} + +type SessionMeasurementRelationListDataType struct { + SessionMeasurementRelationData []SessionMeasurementRelationDataType `json:"sessionMeasurementRelationData,omitempty"` +} + +type SessionMeasurementRelationListDataSelectorsType struct { + SessionId *SessionIdType `json:"sessionId,omitempty"` + MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` +} diff --git a/spine/model/loadcontrol.go b/spine/model/loadcontrol.go index 4d8460b7..a9d0b8fa 100644 --- a/spine/model/loadcontrol.go +++ b/spine/model/loadcontrol.go @@ -29,8 +29,9 @@ type LoadControlLimitIdType uint type LoadControlLimitTypeType string const ( - LoadControlLimitTypeTypeMinValueLimit LoadControlLimitTypeType = "minValueLimit" - LoadControlLimitTypeTypeMaxValueLimit LoadControlLimitTypeType = "maxValueLimit" + LoadControlLimitTypeTypeMinValueLimit LoadControlLimitTypeType = "minValueLimit" + LoadControlLimitTypeTypeMaxValueLimit LoadControlLimitTypeType = "maxValueLimit" + LoadControlLimitTypeTypeSignDependentAbsValueLimit LoadControlLimitTypeType = "signDependentAbsValueLimit" ) type LoadControlCategoryType string diff --git a/spine/model/measurement.go b/spine/model/measurement.go index 657a3636..c41e57ef 100644 --- a/spine/model/measurement.go +++ b/spine/model/measurement.go @@ -115,6 +115,38 @@ type MeasurementListDataSelectorsType struct { TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` } +type MeasurementSeriesDataType struct { + MeasurementId *MeasurementIdType `json:"measurementId,omitempty" eebus:"key"` + ValueType *MeasurementValueTypeType `json:"valueType,omitempty" eebus:"key"` + Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` + Value *ScaledNumberType `json:"value,omitempty"` + EvaluationPeriod *TimePeriodType `json:"evaluationPeriod,omitempty"` + ValueSource *MeasurementValueSourceType `json:"valueSource,omitempty"` + ValueTendency *MeasurementValueTendencyType `json:"valueTendency,omitempty"` + ValueState *MeasurementValueStateType `json:"valueState,omitempty"` +} + +type MeasurementSeriesDataElementsType struct { + MeasurementId *ElementTagType `json:"measurementId,omitempty"` + ValueType *ElementTagType `json:"valueType,omitempty"` + Timestamp *ElementTagType `json:"timestamp,omitempty"` + Value *ElementTagType `json:"value,omitempty"` + EvaluationPeriod *ElementTagType `json:"evaluationPeriod,omitempty"` + ValueSource *ElementTagType `json:"valueSource,omitempty"` + ValueTendency *ElementTagType `json:"valueTendency,omitempty"` + ValueState *ElementTagType `json:"valueState,omitempty"` +} + +type MeasurementSeriesListDataType struct { + MeasurementSeriesData []MeasurementSeriesDataType `json:"measurementSeriesData,omitempty"` +} + +type MeasurementSeriesListDataSelectorsType struct { + MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` + ValueType *MeasurementValueTypeType `json:"valueType,omitempty"` + TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` +} + type MeasurementConstraintsDataType struct { MeasurementId *MeasurementIdType `json:"measurementId,omitempty" eebus:"key"` ValueRangeMin *ScaledNumberType `json:"valueRangeMin,omitempty"` diff --git a/spine/model/setpoint.go b/spine/model/setpoint.go index e403b2b4..deea5198 100644 --- a/spine/model/setpoint.go +++ b/spine/model/setpoint.go @@ -16,6 +16,9 @@ type SetpointDataType struct { ValueMax *ScaledNumberType `json:"valueMax,omitempty"` ValueToleranceAbsolute *ScaledNumberType `json:"valueToleranceAbsolute,omitempty"` ValueTolerancePercentage *ScaledNumberType `json:"valueTolerancePercentage,omitempty"` + IsSetpointChangeable *bool `json:"isSetpointChangeable,omitempty"` + IsSetpointActive *bool `json:"isSetpointActive,omitempty"` + TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` } type SetpointDataElementsType struct { @@ -25,6 +28,9 @@ type SetpointDataElementsType struct { ValueMax *ScaledNumberElementsType `json:"valueMax,omitempty"` ValueToleranceAbsolute *ScaledNumberElementsType `json:"valueToleranceAbsolute,omitempty"` ValueTolerancePercentage *ScaledNumberElementsType `json:"valueTolerancePercentage,omitempty"` + IsSetpointChangeable *ElementTagType `json:"isSetpointChangeable,omitempty"` + IsSetpointActive *ElementTagType `json:"isSetpointActive,omitempty"` + TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` } type SetpointListDataType struct { diff --git a/spine/model/timeseries.go b/spine/model/timeseries.go index 8d2b0c54..91cae18a 100644 --- a/spine/model/timeseries.go +++ b/spine/model/timeseries.go @@ -9,9 +9,13 @@ type TimeSeriesSlotCountType TimeSeriesSlotIdType type TimeSeriesTypeType string const ( - TimeSeriesTypeTypePlan TimeSeriesTypeType = "plan" - TimeSeriesTypeTypeSingleDemand TimeSeriesTypeType = "singleDemand" - TimeSeriesTypeTypeConstraints TimeSeriesTypeType = "constraints" + TimeSeriesTypeTypePlan TimeSeriesTypeType = "plan" + TimeSeriesTypeTypeSingleDemand TimeSeriesTypeType = "singleDemand" + TimeSeriesTypeTypeConstraints TimeSeriesTypeType = "constraints" + TimeSeriesTypeTypeEnergyRequest TimeSeriesTypeType = "energyRequest" + TimeSeriesTypeTypeDischargingEnergyRequest TimeSeriesTypeType = "dischargingEnergyRequest" + TimeSeriesTypeTypeConsumptionLimitCurve TimeSeriesTypeType = "consumptionLimitCurve" + TimeSeriesTypeTypeProductionLimitCurve TimeSeriesTypeType = "productionLimitCurve" ) type TimeSeriesSlotType struct { diff --git a/spine/model/usecaseinformation.go b/spine/model/usecaseinformation.go index e2b9043b..4fcb86fc 100644 --- a/spine/model/usecaseinformation.go +++ b/spine/model/usecaseinformation.go @@ -74,17 +74,19 @@ const ( type UseCaseScenarioSupportType uint type UseCaseSupportType struct { - UseCaseName *UseCaseNameType `json:"useCaseName,omitempty"` - UseCaseVersion *SpecificationVersionType `json:"useCaseVersion,omitempty"` - UseCaseAvailable *bool `json:"useCaseAvailable,omitempty"` - ScenarioSupport []UseCaseScenarioSupportType `json:"scenarioSupport,omitempty"` + UseCaseName *UseCaseNameType `json:"useCaseName,omitempty"` + UseCaseVersion *SpecificationVersionType `json:"useCaseVersion,omitempty"` + UseCaseAvailable *bool `json:"useCaseAvailable,omitempty"` + ScenarioSupport []UseCaseScenarioSupportType `json:"scenarioSupport,omitempty"` + UseCaseDocumentSubRevision *string `json:"useCaseDocumentSubRevision,omitempty"` } type UseCaseSupportElementsType struct { - UseCaseName *ElementTagType `json:"useCaseName,omitempty"` - UseCaseVersion *ElementTagType `json:"useCaseVersion,omitempty"` - UseCaseAvailable *ElementTagType `json:"useCaseAvailable,omitempty"` - ScenarioSupport *ElementTagType `json:"scenarioSupport,omitempty"` + UseCaseName *ElementTagType `json:"useCaseName,omitempty"` + UseCaseVersion *ElementTagType `json:"useCaseVersion,omitempty"` + UseCaseAvailable *ElementTagType `json:"useCaseAvailable,omitempty"` + ScenarioSupport *ElementTagType `json:"scenarioSupport,omitempty"` + UseCaseDocumentSubRevision *ElementTagType `json:"useCaseDocumentSubRevision,omitempty"` } type UseCaseSupportSelectorsType struct { From f5ec8b2d0c6f64f1ee90ecf59f7fc6029a9fbb94 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 5 Sep 2023 23:01:29 +0200 Subject: [PATCH 055/240] Update reported spine version --- spine/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine/const.go b/spine/const.go index 81209919..792d3bc9 100644 --- a/spine/const.go +++ b/spine/const.go @@ -2,4 +2,4 @@ package spine import "github.com/enbility/eebus-go/spine/model" -var SpecificationVersion model.SpecificationVersionType = "1.1.1" +var SpecificationVersion model.SpecificationVersionType = "1.3.0" From b91019ac3bca87c2eef09ff6142f2abf7192c216 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 5 Sep 2023 23:07:54 +0200 Subject: [PATCH 056/240] Update test data --- .../testdata/nm_destinationListData_send_reply_expected.json | 2 +- .../testdata/nm_detaileddiscoverydata_send_read_expected.json | 2 +- .../nm_detaileddiscoverydata_send_reply_expected.json | 4 ++-- .../nm_detaileddiscoverydata_send_result_expected.json | 2 +- .../nm_subscriptionRequestCall_send_result_expected.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/integration_tests/testdata/nm_destinationListData_send_reply_expected.json b/integration_tests/testdata/nm_destinationListData_send_reply_expected.json index 844ed77a..f5c89a46 100644 --- a/integration_tests/testdata/nm_destinationListData_send_reply_expected.json +++ b/integration_tests/testdata/nm_destinationListData_send_reply_expected.json @@ -1,7 +1,7 @@ { "datagram": { "header": { - "specificationVersion": "1.1.1", + "specificationVersion": "1.3.0", "addressSource": { "device": "TestDeviceAddress", "entity": [ diff --git a/integration_tests/testdata/nm_detaileddiscoverydata_send_read_expected.json b/integration_tests/testdata/nm_detaileddiscoverydata_send_read_expected.json index 3f6ead35..02d814c7 100644 --- a/integration_tests/testdata/nm_detaileddiscoverydata_send_read_expected.json +++ b/integration_tests/testdata/nm_detaileddiscoverydata_send_read_expected.json @@ -1,7 +1,7 @@ { "datagram": { "header": { - "specificationVersion": "1.1.1", + "specificationVersion": "1.3.0", "addressSource": { "device": "TestDeviceAddress", "entity": [ diff --git a/integration_tests/testdata/nm_detaileddiscoverydata_send_reply_expected.json b/integration_tests/testdata/nm_detaileddiscoverydata_send_reply_expected.json index e9cf487d..bc363225 100644 --- a/integration_tests/testdata/nm_detaileddiscoverydata_send_reply_expected.json +++ b/integration_tests/testdata/nm_detaileddiscoverydata_send_reply_expected.json @@ -1,7 +1,7 @@ { "datagram": { "header": { - "specificationVersion": "1.1.1", + "specificationVersion": "1.3.0", "addressSource": { "device": "TestDeviceAddress", "entity": [ @@ -26,7 +26,7 @@ "nodeManagementDetailedDiscoveryData": { "specificationVersionList": { "specificationVersion": [ - "1.1.1" + "1.3.0" ] }, "deviceInformation": { diff --git a/integration_tests/testdata/nm_detaileddiscoverydata_send_result_expected.json b/integration_tests/testdata/nm_detaileddiscoverydata_send_result_expected.json index ef0b16d0..33848398 100644 --- a/integration_tests/testdata/nm_detaileddiscoverydata_send_result_expected.json +++ b/integration_tests/testdata/nm_detaileddiscoverydata_send_result_expected.json @@ -1,7 +1,7 @@ { "datagram": { "header": { - "specificationVersion": "1.1.1", + "specificationVersion": "1.3.0", "addressSource": { "device": "TestDeviceAddress", "entity": [ diff --git a/integration_tests/testdata/nm_subscriptionRequestCall_send_result_expected.json b/integration_tests/testdata/nm_subscriptionRequestCall_send_result_expected.json index ac1290bb..9c369b67 100644 --- a/integration_tests/testdata/nm_subscriptionRequestCall_send_result_expected.json +++ b/integration_tests/testdata/nm_subscriptionRequestCall_send_result_expected.json @@ -1,7 +1,7 @@ { "datagram": { "header": { - "specificationVersion": "1.1.1", + "specificationVersion": "1.3.0", "addressSource": { "device": "TestDeviceAddress", "entity": [ From 1cfb748b23397afc5cd84ce6e9e832046d8f32b8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 5 Sep 2023 23:08:45 +0200 Subject: [PATCH 057/240] Add missing model file --- spine/model/stateinformation.go | 115 ++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 spine/model/stateinformation.go diff --git a/spine/model/stateinformation.go b/spine/model/stateinformation.go new file mode 100644 index 00000000..795f303c --- /dev/null +++ b/spine/model/stateinformation.go @@ -0,0 +1,115 @@ +package model + +type StateInformationIdType uint + +type StateInformationType string + +// union StateInformationFunctionalityType StateInformationFunctionalityType +const ( + StateInformationTypeExternalOverrideFromGrid StateInformationType = "externalOverrideFromGrid" + StateInformationTypeAutonomousGridSupport StateInformationType = "autonomousGridSupport" + StateInformationTypeIslandingMode StateInformationType = "islandingMode" + StateInformationTypeBalancing StateInformationType = "balancing" + StateInformationTypeTrickleCharging StateInformationType = "trickleCharging" + StateInformationTypeCalibration StateInformationType = "calibration" + StateInformationTypeCommissioningMissing StateInformationType = "commissioningMissing" + StateInformationTypeSleeping StateInformationType = "sleeping" + StateInformationTypeStarting StateInformationType = "starting" + StateInformationTypeMppt StateInformationType = "mppt" + StateInformationTypeThrottled StateInformationType = "throttled" + StateInformationTypeShuttingDown StateInformationType = "shuttingDown" + StateInformationTypeManualShutdown StateInformationType = "manualShutdown" + StateInformationTypeInverterDefective StateInformationType = "inverterDefective" + StateInformationTypeBatteryOvercurrentProtection StateInformationType = "batteryOvercurrentProtection" + StateInformationTypePvStringOvercurrentProtection StateInformationType = "pvStringOvercurrentProtection" + StateInformationTypeGridFault StateInformationType = "gridFault" + StateInformationTypeGroundFault StateInformationType = "groundFault" + StateInformationTypeAcDisconnected StateInformationType = "acDisconnected" + StateInformationTypeDcDisconnected StateInformationType = "dcDisconnected" + StateInformationTypeCabinetOpen StateInformationType = "cabinetOpen" + StateInformationTypeOverTemperature StateInformationType = "overTemperature" + StateInformationTypeUnderTemperature StateInformationType = "underTemperature" + StateInformationTypeFrequencyAboveLimit StateInformationType = "frequencyAboveLimit" + StateInformationTypeFrequencyBelowLimit StateInformationType = "frequencyBelowLimit" + StateInformationTypeAcVoltageAboveLimit StateInformationType = "acVoltageAboveLimit" + StateInformationTypeAcVoltageBelowLimit StateInformationType = "acVoltageBelowLimit" + StateInformationTypeDcVoltageAboveLimit StateInformationType = "dcVoltageAboveLimit" + StateInformationTypeDcVoltageBelowLimit StateInformationType = "dcVoltageBelowLimit" + StateInformationTypeHardwareTestFailure StateInformationType = "hardwareTestFailure" + StateInformationTypeGenericInternalError StateInformationType = "genericInternalError" +) + +type StateInformationFunctionalityType string + +const ( + StateInformationFunctionalityTypeExternalOverrideFromGrid StateInformationFunctionalityType = "externalOverrideFromGrid" + StateInformationFunctionalityTypeAutonomousGridSupport StateInformationFunctionalityType = "autonomousGridSupport" + StateInformationFunctionalityTypeIslandingMode StateInformationFunctionalityType = "islandingMode" + StateInformationFunctionalityTypeBalancing StateInformationFunctionalityType = "balancing" + StateInformationFunctionalityTypeTrickleCharging StateInformationFunctionalityType = "trickleCharging" + StateInformationFunctionalityTypeCalibration StateInformationFunctionalityType = "calibration" + StateInformationFunctionalityTypeCommissioningMissing StateInformationFunctionalityType = "commissioningMissing" + StateInformationFunctionalityTypeSleeping StateInformationFunctionalityType = "sleeping" + StateInformationFunctionalityTypeStarting StateInformationFunctionalityType = "starting" + StateInformationFunctionalityTypeMppt StateInformationFunctionalityType = "mppt" + StateInformationFunctionalityTypeThrottled StateInformationFunctionalityType = "throttled" + StateInformationFunctionalityTypeShuttingDown StateInformationFunctionalityType = "shuttingDown" + StateInformationFunctionalityTypeManualShutdown StateInformationFunctionalityType = "manualShutdown" +) + +type StateInformationFailureType string + +const ( + StateInformationFailureTypeInverterDefective StateInformationFailureType = "inverterDefective" + StateInformationFailureTypeBatteryOvercurrentProtection StateInformationFailureType = "batteryOvercurrentProtection" + StateInformationFailureTypePvStringOvercurrentProtection StateInformationFailureType = "pvStringOvercurrentProtection" + StateInformationFailureTypeGridFault StateInformationFailureType = "gridFault" + StateInformationFailureTypeGroundFault StateInformationFailureType = "groundFault" + StateInformationFailureTypeAcDisconnected StateInformationFailureType = "acDisconnected" + StateInformationFailureTypeDcDisconnected StateInformationFailureType = "dcDisconnected" + StateInformationFailureTypeCabinetOpen StateInformationFailureType = "cabinetOpen" + StateInformationFailureTypeOverTemperature StateInformationFailureType = "overTemperature" + StateInformationFailureTypeUnderTemperature StateInformationFailureType = "underTemperature" + StateInformationFailureTypeFrequencyAboveLimit StateInformationFailureType = "frequencyAboveLimit" + StateInformationFailureTypeFrequencyBelowLimit StateInformationFailureType = "frequencyBelowLimit" + StateInformationFailureTypeAcVoltageAboveLimit StateInformationFailureType = "acVoltageAboveLimit" + StateInformationFailureTypeAcVoltageBelowLimit StateInformationFailureType = "acVoltageBelowLimit" + StateInformationFailureTypeDcVoltageAboveLimit StateInformationFailureType = "dcVoltageAboveLimit" + StateInformationFailureTypeDcVoltageBelowLimit StateInformationFailureType = "dcVoltageBelowLimit" + StateInformationFailureTypeHardwareTestFailure StateInformationFailureType = "hardwareTestFailure" + StateInformationFailureTypeGenericInternalError StateInformationFailureType = "genericInternalError" +) + +type StateInformationCategoryType string + +const ( + StateInformationCategoryTypeFunctionality StateInformationCategoryType = "functionality" + StateInformationCategoryTypeFailure StateInformationCategoryType = "failure" +) + +type StateInformationDataType struct { + StateInformationId *StateInformationIdType `json:"stateInformationId,omitempty" eebus:"key"` + StateInformation *StateInformationType `json:"stateInformation,omitempty"` + IsActive *bool `json:"isActive,omitempty"` + Category *StateInformationCategoryType `json:"category,omitempty"` + TimeOfLastChange *AbsoluteOrRelativeTimeType `json:"timeOfLastChange,omitempty"` +} + +type StateInformationDataElementsType struct { + StateInformationId *ElementTagType `json:"stateInformationId,omitempty"` + StateInformation *ElementTagType `json:"stateInformation,omitempty"` + IsActive *ElementTagType `json:"isActive,omitempty"` + Category *ElementTagType `json:"category,omitempty"` + TimeOfLastChange *ElementTagType `json:"timeOfLastChange,omitempty"` +} + +type StateInformationListDataType struct { + StateInformationData []StateInformationDataType `json:"stateInformationData,omitempty"` +} + +type StateInformationListDataSelectorsType struct { + StateInformationId *StateInformationIdType `json:"stateInformationId,omitempty"` + StateInformation *StateInformationType `json:"stateInformation,omitempty"` + IsActive *bool `json:"isActive,omitempty"` + Category *StateInformationCategoryType `json:"category,omitempty"` +} From 5134925e48e594d4cd377961c3b4d295af41274e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Sep 2023 14:19:18 +0200 Subject: [PATCH 058/240] Update dependencies --- go.mod | 16 ++++++++-------- go.sum | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 6bfa8fbe..2a1faae4 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,14 @@ go 1.18 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/go-cmp v0.5.9 - github.com/miekg/dns v1.1.52 // indirect + github.com/miekg/dns v1.1.56 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/plural v1.4.1 // indirect - github.com/stretchr/objx v0.5.0 // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/tools v0.7.0 // indirect + github.com/stretchr/objx v0.5.1 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/tools v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -23,8 +23,8 @@ require ( github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.0 github.com/holoplot/go-avahi v1.0.1 - github.com/rickb777/date v1.20.1 - github.com/stretchr/testify v1.8.2 + github.com/rickb777/date v1.20.5 + github.com/stretchr/testify v1.8.4 gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a ) diff --git a/go.sum b/go.sum index d2422101..c9e83cb6 100644 --- a/go.sum +++ b/go.sum @@ -19,21 +19,30 @@ github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfx github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rickb777/date v1.20.1 h1:7MzSOc42Hbr5UXiQOihAAXoYDoeyzr0Hwvt+hCjBDV4= github.com/rickb777/date v1.20.1/go.mod h1:9MqjVxT6a/AQTA4nxj9E6G3ksQiMESTn9/9kfE+CvwU= +github.com/rickb777/date v1.20.5 h1:Ybjz7J7ga9ui4VJizQpil0l330r6wkn6CicaoattIxQ= +github.com/rickb777/date v1.20.5/go.mod h1:6BPrm3/aQI0I8jvlD1fAlm/86k5eSeTQ2mR5FEmTnSw= github.com/rickb777/plural v1.4.1 h1:5MMLcbIaapLFmvDGRT5iPk8877hpTPt8Y9cdSKRw9sU= github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= @@ -42,6 +51,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -49,6 +60,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= @@ -62,6 +75,8 @@ golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -72,6 +87,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From f04454f7a44bb7bec1edee94dd3093bb87e6b417 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 25 Oct 2023 17:21:16 +0200 Subject: [PATCH 059/240] Add missing update support for 1.3 additions --- spine/model/electricalconnection_additions.go | 13 ++++++++++ spine/model/identification_additions.go | 26 +++++++++++++++++++ spine/model/measurement_additions.go | 13 ++++++++++ spine/model/stateinformation_additions.go | 14 ++++++++++ 4 files changed, 66 insertions(+) create mode 100644 spine/model/stateinformation_additions.go diff --git a/spine/model/electricalconnection_additions.go b/spine/model/electricalconnection_additions.go index 4db12443..52e9e8d0 100644 --- a/spine/model/electricalconnection_additions.go +++ b/spine/model/electricalconnection_additions.go @@ -39,6 +39,19 @@ func (r *ElectricalConnectionDescriptionListDataType) UpdateList(newList any, fi r.ElectricalConnectionDescriptionData = UpdateList(r.ElectricalConnectionDescriptionData, newData, filterPartial, filterDelete) } +// ElectricalConnectionCharacteristicListDataType + +var _ Updater = (*ElectricalConnectionCharacteristicListDataType)(nil) + +func (r *ElectricalConnectionCharacteristicListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { + var newData []ElectricalConnectionCharacteristicDataType + if newList != nil { + newData = newList.(*ElectricalConnectionCharacteristicListDataType).ElectricalConnectionCharacteristicListData + } + + r.ElectricalConnectionCharacteristicListData = UpdateList(r.ElectricalConnectionCharacteristicListData, newData, filterPartial, filterDelete) +} + // ElectricalConnectionParameterDescriptionListDataType var _ Updater = (*ElectricalConnectionParameterDescriptionListDataType)(nil) diff --git a/spine/model/identification_additions.go b/spine/model/identification_additions.go index 68be5742..d2ee37c0 100644 --- a/spine/model/identification_additions.go +++ b/spine/model/identification_additions.go @@ -12,3 +12,29 @@ func (r *IdentificationListDataType) UpdateList(newList any, filterPartial, filt r.IdentificationData = UpdateList(r.IdentificationData, newData, filterPartial, filterDelete) } + +// SessionIdentificationListDataType + +var _ Updater = (*SessionIdentificationListDataType)(nil) + +func (r *SessionIdentificationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { + var newData []SessionIdentificationDataType + if newList != nil { + newData = newList.(*SessionIdentificationListDataType).SessionIdentificationData + } + + r.SessionIdentificationData = UpdateList(r.SessionIdentificationData, newData, filterPartial, filterDelete) +} + +// SessionMeasurementRelationListDataType + +var _ Updater = (*SessionMeasurementRelationListDataType)(nil) + +func (r *SessionMeasurementRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { + var newData []SessionMeasurementRelationDataType + if newList != nil { + newData = newList.(*SessionMeasurementRelationListDataType).SessionMeasurementRelationData + } + + r.SessionMeasurementRelationData = UpdateList(r.SessionMeasurementRelationData, newData, filterPartial, filterDelete) +} diff --git a/spine/model/measurement_additions.go b/spine/model/measurement_additions.go index 4d75432e..87cb9055 100644 --- a/spine/model/measurement_additions.go +++ b/spine/model/measurement_additions.go @@ -13,6 +13,19 @@ func (r *MeasurementListDataType) UpdateList(newList any, filterPartial, filterD r.MeasurementData = UpdateList(r.MeasurementData, newData, filterPartial, filterDelete) } +// MeasurementSeriesListDataType + +var _ Updater = (*MeasurementSeriesListDataType)(nil) + +func (r *MeasurementSeriesListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { + var newData []MeasurementSeriesDataType + if newList != nil { + newData = newList.(*MeasurementSeriesListDataType).MeasurementSeriesData + } + + r.MeasurementSeriesData = UpdateList(r.MeasurementSeriesData, newData, filterPartial, filterDelete) +} + // MeasurementConstraintsListDataType var _ Updater = (*MeasurementConstraintsListDataType)(nil) diff --git a/spine/model/stateinformation_additions.go b/spine/model/stateinformation_additions.go new file mode 100644 index 00000000..458e4970 --- /dev/null +++ b/spine/model/stateinformation_additions.go @@ -0,0 +1,14 @@ +package model + +// StateInformationListDataType + +var _ Updater = (*StateInformationListDataType)(nil) + +func (r *StateInformationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { + var newData []StateInformationDataType + if newList != nil { + newData = newList.(*StateInformationListDataType).StateInformationData + } + + r.StateInformationData = UpdateList(r.StateInformationData, newData, filterPartial, filterDelete) +} From 5460066eb2fe595914c7f36acd0c7264a45ea431 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 28 Oct 2023 11:25:15 +0200 Subject: [PATCH 060/240] Update modules --- go.mod | 12 +++++----- go.sum | 73 +++++++++++++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/go.mod b/go.mod index 2a1faae4..3203ef62 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,20 @@ go 1.18 require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/miekg/dns v1.1.56 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/plural v1.4.1 // indirect github.com/stretchr/objx v0.5.1 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.15.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( - github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f + github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df github.com/ahmetb/go-linq/v3 v3.2.0 github.com/godbus/dbus/v5 v5.1.0 github.com/golang/mock v1.6.0 diff --git a/go.sum b/go.sum index c9e83cb6..e8ffd139 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f h1:3+usd0uwuU/PnxDgKiQFc3FSR0C1PnV5Bk03NUKktaU= -github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f/go.mod h1:Fhz5FR1v8Nv4Hj1v9oGXDz4C33+ZaAPqK+Y2wpfWvu0= +github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df h1:qYVips8l0s1ldVoLSup0m+hYh5cMMA4ndcvocxhZuMc= +github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df/go.mod h1:OO5/UahoVBLyauLdDF4httPlSISqbrWDbHi9k99zUsc= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -10,85 +10,96 @@ github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk= github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= -github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rickb777/date v1.20.1 h1:7MzSOc42Hbr5UXiQOihAAXoYDoeyzr0Hwvt+hCjBDV4= -github.com/rickb777/date v1.20.1/go.mod h1:9MqjVxT6a/AQTA4nxj9E6G3ksQiMESTn9/9kfE+CvwU= github.com/rickb777/date v1.20.5 h1:Ybjz7J7ga9ui4VJizQpil0l330r6wkn6CicaoattIxQ= github.com/rickb777/date v1.20.5/go.mod h1:6BPrm3/aQI0I8jvlD1fAlm/86k5eSeTQ2mR5FEmTnSw= github.com/rickb777/plural v1.4.1 h1:5MMLcbIaapLFmvDGRT5iPk8877hpTPt8Y9cdSKRw9sU= github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 7529a6d1a22019cd10f5764d4e4045e0bbac09a0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 28 Oct 2023 12:26:55 +0200 Subject: [PATCH 061/240] Introduce event handler levels To make sure events are handled properly and reliably, the EEBUS core implementation should always be the first level of interacting with incoming SPINE messages, a middleware implementation second and the application last. The EventHandler now provides these 3 levels and Subscribe and Unsubscribe expect the level to be provided. The Core level is only usable with an non public method for safeguard --- spine/device_local.go | 4 +-- spine/events.go | 77 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/spine/device_local.go b/spine/device_local.go index 6c26e3ba..be1ff5f7 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -99,7 +99,7 @@ func (r *DeviceLocalImpl) AddRemoteDevice(ski string, writeI SpineDataConnection // If the request returned an error, it should be retried until it does not // always add subscription, as it checks if it already exists - Events.Subscribe(r) + _ = Events.subscribe(EventHandlerLevelCore, r) return rDevice } @@ -149,7 +149,7 @@ func (r *DeviceLocalImpl) RemoveRemoteDevice(ski string) { // only unsubscribe if we don't have any remote devices left if len(r.remoteDevices) == 0 { - Events.Unsubscribe(r) + _ = Events.unsubscribe(EventHandlerLevelCore, r) } } diff --git a/spine/events.go b/spine/events.go index 103bf4a1..ec1bf870 100644 --- a/spine/events.go +++ b/spine/events.go @@ -1,6 +1,7 @@ package spine import ( + "errors" "sync" "github.com/enbility/eebus-go/spine/model" @@ -8,6 +9,14 @@ import ( var Events events +type EventHandlerLevel uint + +const ( + EventHandlerLevelCore EventHandlerLevel = iota // Shall only be used by the core stack + EventHandlerLevelMiddleware // Shall only be used by middleware implementations, e.g. CEMd + EventHandlerLevelApplication // Shall only be used by applications +) + type ElementChangeType uint16 const ( @@ -41,45 +50,95 @@ type EventHandler interface { HandleEvent(EventPayload) } +type eventHandlerItem struct { + Level EventHandlerLevel + Handler EventHandler +} + type events struct { mu sync.Mutex - handlers []EventHandler + handlers []eventHandlerItem // event handling outside of the core stack } -func (r *events) Subscribe(handler EventHandler) { +// will be used in EEBUS core directly to access the level EventHandlerLevelCore +func (r *events) subscribe(level EventHandlerLevel, handler EventHandler) error { r.mu.Lock() defer r.mu.Unlock() exists := false for _, item := range r.handlers { - if item == handler { + if item.Level == level && item.Handler == handler { exists = true break } } if !exists { - r.handlers = append(r.handlers, handler) + newHandlerItem := eventHandlerItem{ + Level: level, + Handler: handler, + } + r.handlers = append(r.handlers, newHandlerItem) + } + + return nil +} + +// Subscribe to message events and handle them in +// the Eventhandler interface implementation +func (r *events) Subscribe(level EventHandlerLevel, handler EventHandler) error { + if level == EventHandlerLevelCore { + return errors.New("This level is restricted to the EEBUS core implenentation!") } + + return r.subscribe(level, handler) } -func (r *events) Unsubscribe(handler EventHandler) { +// will be used in EEBUS core directly to access the level EventHandlerLevelCore +func (r *events) unsubscribe(level EventHandlerLevel, handler EventHandler) error { r.mu.Lock() defer r.mu.Unlock() - var newHandlers []EventHandler + var newHandlers []eventHandlerItem for _, item := range r.handlers { - if item != handler { + if item.Level != level && item.Handler != handler { newHandlers = append(newHandlers, item) } } + r.handlers = newHandlers + + return nil +} + +// Unsubscribe from getting events +func (r *events) Unsubscribe(level EventHandlerLevel, handler EventHandler) error { + if level == EventHandlerLevelCore { + return errors.New("This level is restricted to the EEBUS core implenentation!") + } + + return r.unsubscribe(level, handler) } +// Publish an event to all subscribers func (r *events) Publish(payload EventPayload) { r.mu.Lock() defer r.mu.Unlock() - for _, handler := range r.handlers { - go handler.HandleEvent(payload) + + // process subscribers by level + handlerLevels := []EventHandlerLevel{ + EventHandlerLevelCore, + EventHandlerLevelMiddleware, + EventHandlerLevelApplication, + } + + for _, level := range handlerLevels { + for _, item := range r.handlers { + if item.Level != level { + continue + } + + go item.Handler.HandleEvent(payload) + } } } From a785445166cb39582d7655d7c9060672278a67b6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 28 Oct 2023 13:11:19 +0200 Subject: [PATCH 062/240] Do not run event handlers asynchronously --- spine/events.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spine/events.go b/spine/events.go index ec1bf870..7e15ec42 100644 --- a/spine/events.go +++ b/spine/events.go @@ -138,7 +138,9 @@ func (r *events) Publish(payload EventPayload) { continue } - go item.Handler.HandleEvent(payload) + // do not run this asynchronously, to make sure all required + // and expected actions are taken + item.Handler.HandleEvent(payload) } } } From 3b80a7a18d41ab4951d85a809b9a0ec416b322b2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 29 Oct 2023 20:14:06 +0100 Subject: [PATCH 063/240] Improve comment of Subscribe method --- spine/events.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spine/events.go b/spine/events.go index 7e15ec42..8659f33c 100644 --- a/spine/events.go +++ b/spine/events.go @@ -86,6 +86,9 @@ func (r *events) subscribe(level EventHandlerLevel, handler EventHandler) error // Subscribe to message events and handle them in // the Eventhandler interface implementation +// +// returns an error if EventHandlerLevelCore is used as +// that is only allowed for internal use func (r *events) Subscribe(level EventHandlerLevel, handler EventHandler) error { if level == EventHandlerLevelCore { return errors.New("This level is restricted to the EEBUS core implenentation!") From b9e8f936c5504c61b93faa4916596a453db4e794 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 14 Nov 2023 14:48:55 +0100 Subject: [PATCH 064/240] Fix panic --- spine/events.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spine/events.go b/spine/events.go index 8659f33c..5079e8c5 100644 --- a/spine/events.go +++ b/spine/events.go @@ -126,7 +126,6 @@ func (r *events) Unsubscribe(level EventHandlerLevel, handler EventHandler) erro // Publish an event to all subscribers func (r *events) Publish(payload EventPayload) { r.mu.Lock() - defer r.mu.Unlock() // process subscribers by level handlerLevels := []EventHandlerLevel{ @@ -146,4 +145,6 @@ func (r *events) Publish(payload EventPayload) { item.Handler.HandleEvent(payload) } } + + r.mu.Unlock() } From 7ce5a49c4eafb57821782989ac18fa9f0a81e139 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 20 Nov 2023 16:15:39 +0100 Subject: [PATCH 065/240] Add support for partial replies --- spine/feature_local.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spine/feature_local.go b/spine/feature_local.go index 6ddc6c4a..e93d9e9d 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -361,7 +361,7 @@ func (r *FeatureLocalImpl) HandleMessage(message *Message) *ErrorType { return err } case model.CmdClassifierTypeReply: - if err := r.processReply(*cmdData.Function, cmdData.Value, message.RequestHeader, message.FeatureRemote); err != nil { + if err := r.processReply(*cmdData.Function, cmdData.Value, message.FilterPartial, message.FilterDelete, message.RequestHeader, message.FeatureRemote); err != nil { return err } case model.CmdClassifierTypeNotify: @@ -435,8 +435,8 @@ func (r *FeatureLocalImpl) processRead(function model.FunctionType, requestHeade return nil } -func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, requestHeader *model.HeaderType, featureRemote *FeatureRemoteImpl) *ErrorType { - featureRemote.UpdateData(function, data, nil, nil) +func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, requestHeader *model.HeaderType, featureRemote *FeatureRemoteImpl) *ErrorType { + featureRemote.UpdateData(function, data, filterPartial, filterDelete) _ = r.pendingRequests.SetData(featureRemote.Device().ski, *requestHeader.MsgCounterReference, data) // an error in SetData only means that there is no pendingRequest waiting for this dataset // so this is nothing to consider as an error to return From 6b1de461038f5f7034b482bdf827392a3861c94f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 08:58:45 +0100 Subject: [PATCH 066/240] Update event handling - do not use multiple public event levels - all public event handlers will be invoked asynchronously - only the core level will be unvoked synchronously --- spine/events.go | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/spine/events.go b/spine/events.go index 5079e8c5..9a499360 100644 --- a/spine/events.go +++ b/spine/events.go @@ -1,7 +1,6 @@ package spine import ( - "errors" "sync" "github.com/enbility/eebus-go/spine/model" @@ -13,7 +12,6 @@ type EventHandlerLevel uint const ( EventHandlerLevelCore EventHandlerLevel = iota // Shall only be used by the core stack - EventHandlerLevelMiddleware // Shall only be used by middleware implementations, e.g. CEMd EventHandlerLevelApplication // Shall only be used by applications ) @@ -89,12 +87,8 @@ func (r *events) subscribe(level EventHandlerLevel, handler EventHandler) error // // returns an error if EventHandlerLevelCore is used as // that is only allowed for internal use -func (r *events) Subscribe(level EventHandlerLevel, handler EventHandler) error { - if level == EventHandlerLevelCore { - return errors.New("This level is restricted to the EEBUS core implenentation!") - } - - return r.subscribe(level, handler) +func (r *events) Subscribe(handler EventHandler) error { + return r.subscribe(EventHandlerLevelApplication, handler) } // will be used in EEBUS core directly to access the level EventHandlerLevelCore @@ -115,12 +109,8 @@ func (r *events) unsubscribe(level EventHandlerLevel, handler EventHandler) erro } // Unsubscribe from getting events -func (r *events) Unsubscribe(level EventHandlerLevel, handler EventHandler) error { - if level == EventHandlerLevelCore { - return errors.New("This level is restricted to the EEBUS core implenentation!") - } - - return r.unsubscribe(level, handler) +func (r *events) Unsubscribe(handler EventHandler) error { + return r.unsubscribe(EventHandlerLevelApplication, handler) } // Publish an event to all subscribers @@ -130,7 +120,6 @@ func (r *events) Publish(payload EventPayload) { // process subscribers by level handlerLevels := []EventHandlerLevel{ EventHandlerLevelCore, - EventHandlerLevelMiddleware, EventHandlerLevelApplication, } @@ -140,9 +129,13 @@ func (r *events) Publish(payload EventPayload) { continue } - // do not run this asynchronously, to make sure all required - // and expected actions are taken - item.Handler.HandleEvent(payload) + if level == EventHandlerLevelCore { + // do not run this asynchronously, to make sure all required + // and expected actions are taken + item.Handler.HandleEvent(payload) + } else { + go item.Handler.HandleEvent(payload) + } } } From 9dae1064bcdfe9605a27404586983403fc9ba151 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 14:17:51 +0100 Subject: [PATCH 067/240] Improve event handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Don’t call handlers in a goroutine - Instead add a mutex lock so only one publish is running at a time - Use a copy of registered handlers, so unsubscribe works in an event handler --- spine/events.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/spine/events.go b/spine/events.go index 9a499360..e01dd65a 100644 --- a/spine/events.go +++ b/spine/events.go @@ -55,6 +55,8 @@ type eventHandlerItem struct { type events struct { mu sync.Mutex + muHandle sync.Mutex + handlers []eventHandlerItem // event handling outside of the core stack } @@ -116,7 +118,12 @@ func (r *events) Unsubscribe(handler EventHandler) error { // Publish an event to all subscribers func (r *events) Publish(payload EventPayload) { r.mu.Lock() + var handler []eventHandlerItem + copy(r.handlers, handler) + r.mu.Unlock() + // Use different locks, so unpublish is possible in the event handlers + r.muHandle.Lock() // process subscribers by level handlerLevels := []EventHandlerLevel{ EventHandlerLevelCore, @@ -129,15 +136,10 @@ func (r *events) Publish(payload EventPayload) { continue } - if level == EventHandlerLevelCore { - // do not run this asynchronously, to make sure all required - // and expected actions are taken - item.Handler.HandleEvent(payload) - } else { - go item.Handler.HandleEvent(payload) - } + // do not run this asynchronously, to make sure all required + // and expected actions are taken + item.Handler.HandleEvent(payload) } } - - r.mu.Unlock() + r.muHandle.Unlock() } From dec59f77473a3c489880aedad90e5c0bb0b4255d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 14:18:10 +0100 Subject: [PATCH 068/240] Add a helper method for eletrical connection --- features/electricalconnection.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/features/electricalconnection.go b/features/electricalconnection.go index ad6fdf37..9b592d22 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -94,6 +94,24 @@ func (e *ElectricalConnection) GetParameterDescriptions() ([]model.ElectricalCon return data.ElectricalConnectionParameterDescriptionData, nil } +// return parameter description for a specific scope +func (e *ElectricalConnection) GetParameterDescriptionForScopeType(scope model.ScopeTypeType) (*model.ElectricalConnectionParameterDescriptionDataType, error) { + desc, err := e.GetParameterDescriptions() + if err != nil { + return nil, err + } + + for _, element := range desc { + if element.ScopeType == nil || *element.ScopeType != scope { + continue + } + + return &element, nil + } + + return nil, ErrDataNotAvailable +} + // return parameter description for a specific parameterId func (e *ElectricalConnection) GetParameterDescriptionForParameterId(parameterId model.ElectricalConnectionParameterIdType) (*model.ElectricalConnectionParameterDescriptionDataType, error) { desc, err := e.GetParameterDescriptions() From b3abc3b5370f86f36bfdc76e5a505967c4a79b05 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 14:24:40 +0100 Subject: [PATCH 069/240] Make sure feature read and writes are safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure reads and writes can’t happen at the same time on a feature --- spine/feature_local.go | 9 +++++++++ spine/feature_remote.go | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/spine/feature_local.go b/spine/feature_local.go index e93d9e9d..885e0a2e 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -70,6 +70,8 @@ type FeatureLocalImpl struct { pendingRequests PendingRequests resultHandler []FeatureResult resultCallback map[model.MsgCounterType]func(result ResultMessage) + + mux sync.Mutex } func NewFeatureLocalImpl(id uint, entity *EntityLocalImpl, ftype model.FeatureTypeType, role model.RoleType) *FeatureLocalImpl { @@ -112,13 +114,20 @@ func (r *FeatureLocalImpl) AddFunctionType(function model.FunctionType, read, wr } func (r *FeatureLocalImpl) Data(function model.FunctionType) any { + r.mux.Lock() + defer r.mux.Unlock() + return r.functionData(function).DataAny() } func (r *FeatureLocalImpl) SetData(function model.FunctionType, data any) { + r.mux.Lock() + fd := r.functionData(function) fd.UpdateDataAny(data, nil, nil) + r.mux.Unlock() + r.Device().NotifySubscribers(r.Address(), fd.NotifyCmdType(nil, nil, false, nil)) } diff --git a/spine/feature_remote.go b/spine/feature_remote.go index 70a11f47..ea82afa3 100644 --- a/spine/feature_remote.go +++ b/spine/feature_remote.go @@ -2,6 +2,7 @@ package spine import ( "fmt" + "sync" "time" "github.com/enbility/eebus-go/logging" @@ -17,6 +18,8 @@ type FeatureRemoteImpl struct { entity *EntityRemoteImpl functionDataMap map[model.FunctionType]FunctionData maxResponseDelay *time.Duration + + mux sync.Mutex } func NewFeatureRemoteImpl(id uint, entity *EntityRemoteImpl, ftype model.FeatureTypeType, role model.RoleType) *FeatureRemoteImpl { @@ -38,10 +41,16 @@ func NewFeatureRemoteImpl(id uint, entity *EntityRemoteImpl, ftype model.Feature } func (r *FeatureRemoteImpl) Data(function model.FunctionType) any { + r.mux.Lock() + defer r.mux.Unlock() + return r.functionData(function).DataAny() } func (r *FeatureRemoteImpl) UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType) { + r.mux.Lock() + defer r.mux.Unlock() + r.functionData(function).UpdateDataAny(data, filterPartial, filterDelete) // TODO: fire event } From f2646ea45ad7b2c14dd2f4661c10791ad1aef930 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 15:44:28 +0100 Subject: [PATCH 070/240] Add a more complex timeseries update test --- spine/model/timeseries_additions_test.go | 105 +++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/spine/model/timeseries_additions_test.go b/spine/model/timeseries_additions_test.go index 688c0645..21630197 100644 --- a/spine/model/timeseries_additions_test.go +++ b/spine/model/timeseries_additions_test.go @@ -58,6 +58,111 @@ func TestTimeSeriesListDataType_Update(t *testing.T) { assert.Equal(t, 1, int(*item2.TimeSeriesSlot[0].TimeSeriesSlotId)) } +func TestTimeSeriesListDataType_Update_02(t *testing.T) { + sut := model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), + TimePeriod: &model.TimePeriodType{ + StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), + EndTime: util.Ptr(model.AbsoluteOrRelativeTimeType("P6D")), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), + TimePeriod: &model.TimePeriodType{ + StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), + EndTime: util.Ptr(model.AbsoluteOrRelativeTimeType("P6D")), + }, + MaxValue: model.NewScaledNumberType(10000), + }, + }, + }, + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), + TimePeriod: &model.TimePeriodType{ + StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: util.Ptr(model.DurationType("P1DT6H46M33S")), + MaxValue: model.NewScaledNumberType(0), + }, + { + TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: util.Ptr(model.DurationType("PT7H37M53S")), + MaxValue: model.NewScaledNumberType(4410), + }, + { + TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(2)), + Duration: util.Ptr(model.DurationType("PT38M")), + MaxValue: model.NewScaledNumberType(0), + }, + { + TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(3)), + Duration: util.Ptr(model.DurationType("PT32M")), + MaxValue: model.NewScaledNumberType(4410), + }, + { + TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(4)), + Duration: util.Ptr(model.DurationType("P1D")), + MaxValue: model.NewScaledNumberType(0), + }, + }, + }, + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), + TimePeriod: &model.TimePeriodType{ + StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: util.Ptr(model.DurationType("P1DT15H24M57S")), + Value: model.NewScaledNumberType(44229), + MaxValue: model.NewScaledNumberType(49629), + }, + }, + }, + }, + } + + newData := model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), + TimePeriod: &model.TimePeriodType{ + StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: util.Ptr(model.DurationType("P1DT15H16M50S")), + Value: model.NewScaledNumberType(11539), + MaxValue: model.NewScaledNumberType(49629), + }, + }, + }, + }, + } + + // Act + sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + + data := sut.TimeSeriesData + // check the non changing items + assert.Equal(t, 3, len(data)) + item1 := data[0] + assert.Equal(t, 1, int(*item1.TimeSeriesId)) + assert.Equal(t, 0, int(*item1.TimeSeriesSlot[0].TimeSeriesSlotId)) + // check properties of updated item + item2 := data[2] + assert.Equal(t, 3, int(*item2.TimeSeriesId)) + assert.Equal(t, 1, int(*item2.TimeSeriesSlot[0].TimeSeriesSlotId)) + assert.Equal(t, 11539, int(item2.TimeSeriesSlot[0].Value.GetValue())) +} + func TestTimeSeriesDescriptionListDataType_Update(t *testing.T) { sut := model.TimeSeriesDescriptionListDataType{ TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ From ffcb09465b979f119f2ce8d00c43e7bb9aca169c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 18:50:59 +0100 Subject: [PATCH 071/240] Add actual support for relative times --- spine/model/commondatatypes_additions.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spine/model/commondatatypes_additions.go b/spine/model/commondatatypes_additions.go index 3486dd47..59838ba5 100644 --- a/spine/model/commondatatypes_additions.go +++ b/spine/model/commondatatypes_additions.go @@ -143,11 +143,17 @@ func (a *AbsoluteOrRelativeTimeType) GetDateTimeType() *DateTimeType { func (a *AbsoluteOrRelativeTimeType) GetTime() (time.Time, error) { value := NewDateTimeType(string(*a)) t, err := value.GetTime() + if err == nil { + return t, nil + } + + // Check if this is a relative time + d, err := getTimeDurationFromString(string(*a)) if err != nil { return time.Time{}, err } - - return t, nil + r := time.Now().Add(d) + return r, nil } func (a *AbsoluteOrRelativeTimeType) GetDurationType() (*DurationType, error) { From 125fbc0c562cc409aa3575d3b93c4efcb6429a38 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 30 Nov 2023 13:23:20 +0100 Subject: [PATCH 072/240] Update go-avahi to fix a crash See https://github.com/holoplot/go-avahi/pull/19 --- go.mod | 2 ++ go.sum | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3203ef62..b54433a2 100644 --- a/go.mod +++ b/go.mod @@ -32,3 +32,5 @@ retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. ) + +replace github.com/holoplot/go-avahi => github.com/derandereandi/go-avahi v0.0.0-20231130121746-194d27d20d26 // temp replace with bugfix for crashes https://github.com/holoplot/go-avahi/pull/19 diff --git a/go.sum b/go.sum index e8ffd139..afe3e196 100644 --- a/go.sum +++ b/go.sum @@ -5,7 +5,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/derandereandi/go-avahi v0.0.0-20231130121746-194d27d20d26 h1:/YeKzVqhsG384tE9S41hNgryCmFzuBaXG0NddJ/75NE= +github.com/derandereandi/go-avahi v0.0.0-20231130121746-194d27d20d26/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -14,8 +15,6 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk= -github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= From f20b84229df0cb2032b59fb2bdca3b97ff7f585c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 30 Nov 2023 13:23:51 +0100 Subject: [PATCH 073/240] Extend avahi mdns debug message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show the mDNS service avahi couldn’t resolve --- service/mdns/avahi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/mdns/avahi.go b/service/mdns/avahi.go index 27bba30a..1f0e5c5b 100644 --- a/service/mdns/avahi.go +++ b/service/mdns/avahi.go @@ -155,7 +155,7 @@ func (a *AvahiProvider) processService(service avahi.Service, remove bool, callb resolved, err := a.avServer.ResolveService(service.Interface, service.Protocol, service.Name, service.Type, service.Domain, avahi.ProtoUnspec, 0) if err != nil { - logging.Log.Debug("avahi - error resolving service:", err) + logging.Log.Debug("avahi - error resolving service:", service, "error:", err) return } From f19d0d9962e20f69989cc9210ade2730d9c31b76 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 30 Nov 2023 19:48:58 +0100 Subject: [PATCH 074/240] Update go-avahi --- go.mod | 4 +--- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index b54433a2..65542b8e 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.0 - github.com/holoplot/go-avahi v1.0.1 + github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed github.com/rickb777/date v1.20.5 github.com/stretchr/testify v1.8.4 gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a @@ -32,5 +32,3 @@ retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. ) - -replace github.com/holoplot/go-avahi => github.com/derandereandi/go-avahi v0.0.0-20231130121746-194d27d20d26 // temp replace with bugfix for crashes https://github.com/holoplot/go-avahi/pull/19 diff --git a/go.sum b/go.sum index afe3e196..22747245 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,6 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/derandereandi/go-avahi v0.0.0-20231130121746-194d27d20d26 h1:/YeKzVqhsG384tE9S41hNgryCmFzuBaXG0NddJ/75NE= -github.com/derandereandi/go-avahi v0.0.0-20231130121746-194d27d20d26/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -15,6 +13,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed h1:AMm8KKtfeEhUlj45DYJBSMW2VcLO1Tss3jaMUqb+VvE= +github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= From 335c5c659f7b7221d9e6c46901672985066a7369 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 29 Dec 2023 20:56:10 +0100 Subject: [PATCH 075/240] Add missing SPINE 1.3 function to feature mappings --- spine/function_data_factory.go | 10 ++++++++++ spine/function_data_factory_test.go | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/spine/function_data_factory.go b/spine/function_data_factory.go index 6b9520db..3e4102b0 100644 --- a/spine/function_data_factory.go +++ b/spine/function_data_factory.go @@ -88,6 +88,7 @@ func CreateFunctionData[F any](featureType model.FeatureTypeType) []F { createFunctionData[model.ElectricalConnectionParameterDescriptionListDataType, F](model.FunctionTypeElectricalConnectionParameterDescriptionListData), createFunctionData[model.ElectricalConnectionPermittedValueSetListDataType, F](model.FunctionTypeElectricalConnectionPermittedValueSetListData), createFunctionData[model.ElectricalConnectionStateListDataType, F](model.FunctionTypeElectricalConnectionStateListData), + createFunctionData[model.ElectricalConnectionCharacteristicListDataType, F](model.FunctionTypeElectricalConnectionCharacteristicListData), }...) } @@ -107,6 +108,8 @@ func CreateFunctionData[F any](featureType model.FeatureTypeType) []F { if featureType == model.FeatureTypeTypeIdentification || featureType == model.FeatureTypeTypeGeneric { result = append(result, []F{ createFunctionData[model.IdentificationListDataType, F](model.FunctionTypeIdentificationListData), + createFunctionData[model.SessionIdentificationListDataType, F](model.FunctionTypeSessionIdentificationListData), + createFunctionData[model.SessionMeasurementRelationListDataType, F](model.FunctionTypeSessionMeasurementRelationListData), }...) } @@ -135,6 +138,7 @@ func CreateFunctionData[F any](featureType model.FeatureTypeType) []F { createFunctionData[model.MeasurementDescriptionListDataType, F](model.FunctionTypeMeasurementDescriptionListData), createFunctionData[model.MeasurementConstraintsListDataType, F](model.FunctionTypeMeasurementConstraintsListData), createFunctionData[model.MeasurementThresholdRelationListDataType, F](model.FunctionTypeMeasurementThresholdRelationListData), + createFunctionData[model.MeasurementSeriesListDataType, F](model.FunctionTypeMeasurementSeriesListData), }...) } @@ -214,6 +218,12 @@ func CreateFunctionData[F any](featureType model.FeatureTypeType) []F { }...) } + if featureType == model.FeatureTypeTypeStateInformation || featureType == model.FeatureTypeTypeGeneric { + result = append(result, []F{ + createFunctionData[model.StateInformationListDataType, F](model.FunctionTypeStateInformationListData), + }...) + } + if featureType == model.FeatureTypeTypeSupplyCondition || featureType == model.FeatureTypeTypeGeneric { result = append(result, []F{ createFunctionData[model.SupplyConditionDescriptionListDataType, F](model.FunctionTypeSupplyConditionDescriptionListData), diff --git a/spine/function_data_factory_test.go b/spine/function_data_factory_test.go index 368b8562..ef9f35f0 100644 --- a/spine/function_data_factory_test.go +++ b/spine/function_data_factory_test.go @@ -31,7 +31,7 @@ func TestFunctionDataFactory_FunctionData(t *testing.T) { assert.IsType(t, &FunctionDataImpl[model.DeviceDiagnosisHeartbeatDataType]{}, result[1]) result = CreateFunctionData[FunctionData](model.FeatureTypeTypeElectricalConnection) - assert.Equal(t, 4, len(result)) + assert.Equal(t, 5, len(result)) assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionDescriptionListDataType]{}, result[0]) assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionParameterDescriptionListDataType]{}, result[1]) assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionPermittedValueSetListDataType]{}, result[2]) @@ -45,7 +45,7 @@ func TestFunctionDataFactory_FunctionData(t *testing.T) { assert.IsType(t, &FunctionDataImpl[model.HvacSystemFunctionListDataType]{}, result[4]) result = CreateFunctionData[FunctionData](model.FeatureTypeTypeIdentification) - assert.Equal(t, 1, len(result)) + assert.Equal(t, 3, len(result)) assert.IsType(t, &FunctionDataImpl[model.IdentificationListDataType]{}, result[0]) result = CreateFunctionData[FunctionData](model.FeatureTypeTypeIncentiveTable) @@ -62,7 +62,7 @@ func TestFunctionDataFactory_FunctionData(t *testing.T) { assert.IsType(t, &FunctionDataImpl[model.LoadControlLimitListDataType]{}, result[3]) result = CreateFunctionData[FunctionData](model.FeatureTypeTypeMeasurement) - assert.Equal(t, 4, len(result)) + assert.Equal(t, 5, len(result)) assert.IsType(t, &FunctionDataImpl[model.MeasurementListDataType]{}, result[0]) assert.IsType(t, &FunctionDataImpl[model.MeasurementDescriptionListDataType]{}, result[1]) assert.IsType(t, &FunctionDataImpl[model.MeasurementConstraintsListDataType]{}, result[2]) @@ -75,7 +75,7 @@ func TestFunctionDataFactory_FunctionData(t *testing.T) { assert.IsType(t, &FunctionDataImpl[model.TimeSeriesListDataType]{}, result[2]) result = CreateFunctionData[FunctionData](model.FeatureTypeTypeGeneric) - assert.Equal(t, 118, len(result)) + assert.Equal(t, 123, len(result)) } func TestFunctionDataFactory_FunctionDataCmd(t *testing.T) { From a56f83821f31d6a67e5fbd197f255c4de1271175 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 29 Dec 2023 21:23:46 +0100 Subject: [PATCH 076/240] Major updates - Rewrite heartbeat management - Runs on the local device as binding and subscription manager - Updates local data function data store, which will then automatically trigger notifications to all subscribers - Only handles heartbeat creation (and sending)! - Ensures heartbeat related requirements (heartbeat id global, data only updated on an actual heartbeat, etc.) - A heartbeat interval has to be provided when initialising the service! - Heartbeats automatically stop/resume depending on existing subscribers - Heartbeat Manager currently only works for a single entity requiring/providing heartbeats - Update binding and subscription managers - Add tests - Use sync.Mutex to avoid race conditions - Use new DeepCopy helper to allow safe editing of model data for its own usage reasons and avoid crashes - Fix non working Remove implementations - Pass localDevice on initialisation instead on each function call - Update service configuration - Add list of entityTypes that will be created, at least 1 is required to be provided - Add heartbeat timeout - Update services API - Remove publich functions that just pass through accessible localDevice functions - Update features - update feature creation API to require corresponding local entity instead of local device - move tests into features_test package - Update DeviceLocalImpl API - remove FeatureByTypeAndRole, instead the EntityLocalImpl FeatureOfTypeAndRole should be used - Add a new helper `DeepCopy` to easily copy SPINE model data structures, which is sometimes required to be used to avoid race conditions - Update spine sender - Add local cache for sent notify messages and public interface method to search the cache - The cache allows to find notify messages causing the remote device to send an error message != 0 and implement some means of error handling for these - Fix linter warning because of duplicate file names for mock and implementation - Update generated mock - Add tests for EntityLocalImpl - Various data race fixes - Update various pieces to use new/changed APIs --- cmd/evse/main.go | 4 +- cmd/hems/main.go | 4 +- features/deviceclassification.go | 4 +- features/deviceclassification_test.go | 11 +- features/deviceconfiguration.go | 4 +- features/deviceconfiguration_test.go | 11 +- features/devicediagnosis.go | 4 +- features/devicediagnosis_test.go | 11 +- features/electricalconnection.go | 4 +- features/electricalconnection_test.go | 11 +- features/feature.go | 20 ++- features/helper_test.go | 13 +- features/identification.go | 4 +- features/identification_test.go | 11 +- features/incentivetable.go | 4 +- features/incentivetable_test.go | 11 +- features/loadcontrol.go | 4 +- features/loadcontrol_test.go | 11 +- features/measurement.go | 4 +- features/measurement_test.go | 11 +- features/timeseries.go | 4 +- features/timeseries_test.go | 11 +- go.mod | 1 + go.sum | 2 + .../emobility_measurement_test.go | 22 +-- integration_tests/helper_test.go | 3 +- integration_tests/nodemanagement_test.go | 3 +- service/service.go | 55 +----- service/types.go | 23 ++- ship/connection_test.go | 3 +- ship/hs_helper_test.go | 3 +- spine/binding_manager.go | 71 ++++++-- spine/binding_manager_test.go | 82 +++++++++ spine/device_local.go | 66 +++---- spine/device_local_test.go | 11 +- spine/device_remote.go | 40 ++--- spine/entity_local_test.go | 57 ++++++ spine/feature_local_test.go | 9 +- spine/feature_remote_test.go | 21 +-- spine/heartbeat.go | 148 --------------- spine/heartbeat_manager.go | 169 ++++++++++++++++++ spine/heartbeat_manager_test.go | 139 ++++++++++++++ spine/mocks/Sender.go | 35 +++- spine/nodemanagement_binding.go | 2 +- spine/nodemanagement_defaileddiscovery.go | 6 +- spine/nodemanagement_subscription.go | 17 +- spine/nodemanagement_test.go | 4 +- spine/{sender.go => send.go} | 24 ++- spine/{sender_test.go => send_test.go} | 21 ++- spine/subscription_manager.go | 47 ++++- spine/subscription_manager_test.go | 82 +++++++++ util/helper.go | 11 +- 52 files changed, 936 insertions(+), 417 deletions(-) create mode 100644 spine/binding_manager_test.go create mode 100644 spine/entity_local_test.go delete mode 100644 spine/heartbeat.go create mode 100644 spine/heartbeat_manager.go create mode 100644 spine/heartbeat_manager_test.go rename spine/{sender.go => send.go} (89%) rename spine/{sender_test.go => send_test.go} (63%) create mode 100644 spine/subscription_manager_test.go diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 29d68d33..e2f1a44a 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -63,7 +63,9 @@ func (h *evse) run() { configuration, err := service.NewConfiguration( "Demo", "Demo", "EVSE", "234567890", - model.DeviceTypeTypeChargingStation, port, certificate, 230) + model.DeviceTypeTypeChargingStation, + []model.EntityTypeType{model.EntityTypeTypeEVSE}, + port, certificate, 230, time.Second*4) if err != nil { log.Fatal(err) } diff --git a/cmd/hems/main.go b/cmd/hems/main.go index c4730083..b09b7b77 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -63,7 +63,9 @@ func (h *hems) run() { configuration, err := service.NewConfiguration( "Demo", "Demo", "HEMS", "123456789", - model.DeviceTypeTypeEnergyManagementSystem, port, certificate, 230) + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + port, certificate, 230, time.Second*4) if err != nil { log.Fatal(err) } diff --git a/features/deviceclassification.go b/features/deviceclassification.go index c2f5f9c6..4cbfe850 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -9,8 +9,8 @@ type DeviceClassification struct { *FeatureImpl } -func NewDeviceClassification(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*DeviceClassification, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, spineLocalDevice, entity) +func NewDeviceClassification(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceClassification, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/deviceclassification_test.go b/features/deviceclassification_test.go index 48a5c63a..f129c716 100644 --- a/features/deviceclassification_test.go +++ b/features/deviceclassification_test.go @@ -1,8 +1,9 @@ -package features +package features_test import ( "testing" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -17,10 +18,10 @@ func TestDeviceClassificationSuite(t *testing.T) { type DeviceClassificationSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - deviceClassification *DeviceClassification + deviceClassification *features.DeviceClassification sentMessage []byte } @@ -31,7 +32,7 @@ func (s *DeviceClassificationSuite) WriteSpineMessage(message []byte) { } func (s *DeviceClassificationSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -45,7 +46,7 @@ func (s *DeviceClassificationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceClassification, err = NewDeviceClassification(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.deviceClassification, err = features.NewDeviceClassification(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceClassification) } diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index dc63a6ac..5247302c 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -9,8 +9,8 @@ type DeviceConfiguration struct { *FeatureImpl } -func NewDeviceConfiguration(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*DeviceConfiguration, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, spineLocalDevice, entity) +func NewDeviceConfiguration(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceConfiguration, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index 22b40e7f..45af5b5f 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -1,8 +1,9 @@ -package features +package features_test import ( "testing" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -17,10 +18,10 @@ func TestDeviceConfigurationSuite(t *testing.T) { type DeviceConfigurationSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - deviceConfiguration *DeviceConfiguration + deviceConfiguration *features.DeviceConfiguration sentMessage []byte } @@ -31,7 +32,7 @@ func (s *DeviceConfigurationSuite) WriteSpineMessage(message []byte) { } func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -46,7 +47,7 @@ func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceConfiguration, err = NewDeviceConfiguration(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.deviceConfiguration, err = features.NewDeviceConfiguration(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceConfiguration) } diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index 701cca58..a9261056 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -9,8 +9,8 @@ type DeviceDiagnosis struct { *FeatureImpl } -func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*DeviceDiagnosis, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, spineLocalDevice, entity) +func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceDiagnosis, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 44047f2c..103175f1 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -1,8 +1,9 @@ -package features +package features_test import ( "testing" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -17,10 +18,10 @@ func TestDeviceDiagnosisSuite(t *testing.T) { type DeviceDiagnosisSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - deviceDiagnosis *DeviceDiagnosis + deviceDiagnosis *features.DeviceDiagnosis sentMessage []byte } @@ -31,7 +32,7 @@ func (s *DeviceDiagnosisSuite) WriteSpineMessage(message []byte) { } func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -45,7 +46,7 @@ func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceDiagnosis, err = NewDeviceDiagnosis(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.deviceDiagnosis, err = features.NewDeviceDiagnosis(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceDiagnosis) } diff --git a/features/electricalconnection.go b/features/electricalconnection.go index 9b592d22..8c07b6be 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -9,8 +9,8 @@ type ElectricalConnection struct { *FeatureImpl } -func NewElectricalConnection(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*ElectricalConnection, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, spineLocalDevice, entity) +func NewElectricalConnection(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*ElectricalConnection, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index a0d88d84..91add2a4 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -1,8 +1,9 @@ -package features +package features_test import ( "testing" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -17,10 +18,10 @@ func TestElectricalConnectionSuite(t *testing.T) { type ElectricalConnectionSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - electricalConnection *ElectricalConnection + electricalConnection *features.ElectricalConnection sentMessage []byte } @@ -31,7 +32,7 @@ func (s *ElectricalConnectionSuite) WriteSpineMessage(message []byte) { } func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -47,7 +48,7 @@ func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) { ) var err error - s.electricalConnection, err = NewElectricalConnection(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.electricalConnection, err = features.NewElectricalConnection(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.electricalConnection) } diff --git a/features/feature.go b/features/feature.go index 5f37f081..20ea1c57 100644 --- a/features/feature.go +++ b/features/feature.go @@ -19,24 +19,26 @@ type FeatureImpl struct { remoteRole model.RoleType spineLocalDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl featureLocal spine.FeatureLocal featureRemote *spine.FeatureRemoteImpl - device *spine.DeviceRemoteImpl - entity *spine.EntityRemoteImpl + remoteDevice *spine.DeviceRemoteImpl + remoteEntity *spine.EntityRemoteImpl } var _ Feature = (*FeatureImpl)(nil) -func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*FeatureImpl, error) { +func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*FeatureImpl, error) { f := &FeatureImpl{ featureType: featureType, localRole: localRole, remoteRole: remoteRole, - spineLocalDevice: spineLocalDevice, - device: entity.Device(), - entity: entity, + spineLocalDevice: localEntity.Device(), + localEntity: localEntity, + remoteDevice: remoteEntity.Device(), + remoteEntity: remoteEntity, } var err error @@ -95,12 +97,12 @@ func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, el // internal helper method for getting local and remote feature for a given featureType and a given remoteDevice func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (spine.FeatureLocal, *spine.FeatureRemoteImpl, error) { - if f.entity == nil { + if f.remoteEntity == nil { return nil, nil, errors.New("invalid remote entity provided") } - featureLocal := f.spineLocalDevice.FeatureByTypeAndRole(f.featureType, f.localRole) - featureRemote := f.entity.Device().FeatureByEntityTypeAndRole(f.entity, f.featureType, f.remoteRole) + featureLocal := f.localEntity.FeatureOfTypeAndRole(f.featureType, f.localRole) + featureRemote := f.remoteEntity.Device().FeatureByEntityTypeAndRole(f.remoteEntity, f.featureType, f.remoteRole) if featureLocal == nil { return nil, nil, errors.New("local feature not found") diff --git a/features/helper_test.go b/features/helper_test.go index fc7cf975..ac2ecc71 100644 --- a/features/helper_test.go +++ b/features/helper_test.go @@ -1,6 +1,8 @@ -package features +package features_test import ( + "time" + "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -12,9 +14,9 @@ type featureFunctions struct { functions []model.FunctionType } -func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, featureFunctions []featureFunctions) (*spine.DeviceLocalImpl, *spine.EntityRemoteImpl) { +func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, featureFunctions []featureFunctions) (*spine.EntityLocalImpl, *spine.EntityRemoteImpl) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) + "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) localDevice.AddEntity(localEntity) @@ -24,7 +26,8 @@ func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, feature } remoteDeviceName := "remoteDevice" - remoteDevice := spine.NewDeviceRemoteImpl(localDevice, "test", dataCon) + sender := spine.NewSender(dataCon) + remoteDevice := spine.NewDeviceRemoteImpl(localDevice, "test", sender) data := &model.NodeManagementDetailedDiscoveryDataType{ DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ Description: &model.NetworkManagementDeviceDescriptionDataType{ @@ -81,5 +84,5 @@ func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, feature assert.NotNil(t, remoteEntities) assert.NotEqual(t, 0, len(remoteEntities)) - return localDevice, remoteEntities[0] + return localEntity, remoteEntities[0] } diff --git a/features/identification.go b/features/identification.go index 0225febf..bf925fa4 100644 --- a/features/identification.go +++ b/features/identification.go @@ -9,8 +9,8 @@ type Identification struct { *FeatureImpl } -func NewIdentification(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*Identification, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeIdentification, localRole, remoteRole, spineLocalDevice, entity) +func NewIdentification(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*Identification, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeIdentification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/identification_test.go b/features/identification_test.go index 37f7942a..4f60a646 100644 --- a/features/identification_test.go +++ b/features/identification_test.go @@ -1,8 +1,9 @@ -package features +package features_test import ( "testing" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -17,10 +18,10 @@ func TestIdentificationSuite(t *testing.T) { type IdentificationSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - identification *Identification + identification *features.Identification sentMessage []byte } @@ -31,7 +32,7 @@ func (s *IdentificationSuite) WriteSpineMessage(message []byte) { } func (s *IdentificationSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -45,7 +46,7 @@ func (s *IdentificationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.identification, err = NewIdentification(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.identification, err = features.NewIdentification(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.identification) } diff --git a/features/incentivetable.go b/features/incentivetable.go index a39713ca..c31294f4 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -9,8 +9,8 @@ type IncentiveTable struct { *FeatureImpl } -func NewIncentiveTable(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*IncentiveTable, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, spineLocalDevice, entity) +func NewIncentiveTable(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*IncentiveTable, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 2f5d03b5..707bf80e 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -1,8 +1,9 @@ -package features +package features_test import ( "testing" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -17,10 +18,10 @@ func TestIncentiveTableSuite(t *testing.T) { type IncentiveTableSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - incentiveTable *IncentiveTable + incentiveTable *features.IncentiveTable sentMessage []byte } @@ -31,7 +32,7 @@ func (s *IncentiveTableSuite) WriteSpineMessage(message []byte) { } func (s *IncentiveTableSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -47,7 +48,7 @@ func (s *IncentiveTableSuite) BeforeTest(suiteName, testName string) { ) var err error - s.incentiveTable, err = NewIncentiveTable(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.incentiveTable, err = features.NewIncentiveTable(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.incentiveTable) } diff --git a/features/loadcontrol.go b/features/loadcontrol.go index feca212f..58f9394d 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -9,8 +9,8 @@ type LoadControl struct { *FeatureImpl } -func NewLoadControl(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*LoadControl, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeLoadControl, localRole, remoteRole, spineLocalDevice, entity) +func NewLoadControl(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*LoadControl, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeLoadControl, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index 75200e5a..c27d80fa 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -1,8 +1,9 @@ -package features +package features_test import ( "testing" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -17,10 +18,10 @@ func TestLoadControlSuite(t *testing.T) { type LoadControlSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - loadControl *LoadControl + loadControl *features.LoadControl sentMessage []byte } @@ -31,7 +32,7 @@ func (s *LoadControlSuite) WriteSpineMessage(message []byte) { } func (s *LoadControlSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -47,7 +48,7 @@ func (s *LoadControlSuite) BeforeTest(suiteName, testName string) { ) var err error - s.loadControl, err = NewLoadControl(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.loadControl, err = features.NewLoadControl(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.loadControl) } diff --git a/features/measurement.go b/features/measurement.go index 8dacef96..deb69e0c 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -9,8 +9,8 @@ type Measurement struct { *FeatureImpl } -func NewMeasurement(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*Measurement, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeMeasurement, localRole, remoteRole, spineLocalDevice, entity) +func NewMeasurement(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*Measurement, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeMeasurement, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/measurement_test.go b/features/measurement_test.go index a8c1c377..e10ed568 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -1,9 +1,10 @@ -package features +package features_test import ( "testing" "time" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -18,10 +19,10 @@ func TestMeasurementSuite(t *testing.T) { type MeasurementSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - measurement *Measurement + measurement *features.Measurement sentMessage []byte } @@ -32,7 +33,7 @@ func (s *MeasurementSuite) WriteSpineMessage(message []byte) { } func (s *MeasurementSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -56,7 +57,7 @@ func (s *MeasurementSuite) BeforeTest(suiteName, testName string) { ) var err error - s.measurement, err = NewMeasurement(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.measurement, err = features.NewMeasurement(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.measurement) } diff --git a/features/timeseries.go b/features/timeseries.go index 7b3d02c2..88ec13c1 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -9,8 +9,8 @@ type TimeSeries struct { *FeatureImpl } -func NewTimeSeries(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*TimeSeries, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeTimeSeries, localRole, remoteRole, spineLocalDevice, entity) +func NewTimeSeries(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*TimeSeries, error) { + feature, err := NewFeatureImpl(model.FeatureTypeTypeTimeSeries, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/timeseries_test.go b/features/timeseries_test.go index 97c0a107..0b769380 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -1,9 +1,10 @@ -package features +package features_test import ( "testing" "time" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -18,10 +19,10 @@ func TestTimeSeriesSuite(t *testing.T) { type TimeSeriesSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl remoteEntity *spine.EntityRemoteImpl - timeSeries *TimeSeries + timeSeries *features.TimeSeries sentMessage []byte } @@ -32,7 +33,7 @@ func (s *TimeSeriesSuite) WriteSpineMessage(message []byte) { } func (s *TimeSeriesSuite) BeforeTest(suiteName, testName string) { - s.localDevice, s.remoteEntity = setupFeatures( + s.localEntity, s.remoteEntity = setupFeatures( s.T(), s, []featureFunctions{ @@ -48,7 +49,7 @@ func (s *TimeSeriesSuite) BeforeTest(suiteName, testName string) { ) var err error - s.timeSeries, err = NewTimeSeries(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity) + s.timeSeries, err = features.NewTimeSeries(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.timeSeries) } diff --git a/go.mod b/go.mod index 65542b8e..3206f2d2 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.0 + github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed github.com/rickb777/date v1.20.5 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index 22747245..bac29a91 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed h1:AMm8KKtfeEhUlj45DYJBSMW2VcLO1Tss3jaMUqb+VvE= github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= diff --git a/integration_tests/emobility_measurement_test.go b/integration_tests/emobility_measurement_test.go index f64d96f1..fc29d7ff 100644 --- a/integration_tests/emobility_measurement_test.go +++ b/integration_tests/emobility_measurement_test.go @@ -2,6 +2,7 @@ package integrationtests import ( "testing" + "time" "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/spine" @@ -18,7 +19,8 @@ type EmobilityMeasurementSuite struct { suite.Suite spine.SpineDataConnection - sut *spine.DeviceLocalImpl + sut *spine.DeviceLocalImpl + localEntity *spine.EntityLocalImpl measurement *features.Measurement electricalconnection *features.ElectricalConnection @@ -34,14 +36,14 @@ func (s *EmobilityMeasurementSuite) SetupSuite() { func (s *EmobilityMeasurementSuite) BeforeTest(suiteName, testName string) { s.sut = spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) - localEntity := spine.NewEntityLocalImpl(s.sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) - s.sut.AddEntity(localEntity) + "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + s.localEntity = spine.NewEntityLocalImpl(s.sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + s.sut.AddEntity(s.localEntity) - f := spine.NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) + f := spine.NewFeatureLocalImpl(1, s.localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + s.localEntity.AddFeature(f) + f = spine.NewFeatureLocalImpl(2, s.localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + s.localEntity.AddFeature(f) s.remoteSki = "TestRemoteSki" @@ -59,10 +61,10 @@ func (s *EmobilityMeasurementSuite) TestGetValuesPerPhaseForScope() { assert.NotNil(s.T(), remoteEntity) var err error - s.measurement, err = features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, s.sut, remoteEntity) + s.measurement, err = features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, remoteEntity) assert.Nil(s.T(), err) - s.electricalconnection, err = features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, s.sut, remoteEntity) + s.electricalconnection, err = features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, remoteEntity) assert.Nil(s.T(), err) // Act diff --git a/integration_tests/helper_test.go b/integration_tests/helper_test.go index 6c3dd2e1..6d09d057 100644 --- a/integration_tests/helper_test.go +++ b/integration_tests/helper_test.go @@ -6,6 +6,7 @@ import ( "os" "sync" "testing" + "time" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" @@ -99,7 +100,7 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg func beforeTest(suiteName, testName string, fId uint, ftype model.FeatureTypeType, frole model.RoleType) (*spine.DeviceLocalImpl, string, spine.SpineDataProcessing, *WriteMessageHandler) { sut := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) + "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) sut.AddEntity(localEntity) f := spine.NewFeatureLocalImpl(fId, localEntity, ftype, frole) diff --git a/integration_tests/nodemanagement_test.go b/integration_tests/nodemanagement_test.go index 00b1009a..01733fa2 100644 --- a/integration_tests/nodemanagement_test.go +++ b/integration_tests/nodemanagement_test.go @@ -2,6 +2,7 @@ package integrationtests import ( "testing" + "time" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" @@ -40,7 +41,7 @@ func (s *NodeManagementSuite) SetupSuite() { func (s *NodeManagementSuite) BeforeTest(suiteName, testName string) { s.sut = spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) + "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) s.remoteSki = "TestRemoteSki" s.writeHandler = &WriteMessageHandler{} diff --git a/service/service.go b/service/service.go index 80d843f1..cf164be0 100644 --- a/service/service.go +++ b/service/service.go @@ -197,21 +197,16 @@ func (s *EEBUSService) Setup() error { deviceAdress, sd.deviceType, sd.featureSet, + sd.heartbeatTimeout, ) - // Create the device entity and add it to the SPINE device - entityAddress := []model.AddressEntityType{1} - var entityType model.EntityTypeType - switch sd.deviceType { - case model.DeviceTypeTypeEnergyManagementSystem: - entityType = model.EntityTypeTypeCEM - case model.DeviceTypeTypeChargingStation: - entityType = model.EntityTypeTypeEVSE - default: - logging.Log.Errorf("Unknown device type: %s", sd.deviceType) + // Create the device entities and add it to the SPINE device + for _, entityType := range sd.entityTypes { + entityAddressId := model.AddressEntityType(len(s.spineLocalDevice.Entities())) + entityAddress := []model.AddressEntityType{entityAddressId} + entity := spine.NewEntityLocalImpl(s.spineLocalDevice, entityType, entityAddress) + s.spineLocalDevice.AddEntity(entity) } - entity := spine.NewEntityLocalImpl(s.spineLocalDevice, entityType, entityAddress) - s.spineLocalDevice.AddEntity(entity) // setup mDNS mdns := newMDNS(s.LocalService.SKI, s.Configuration) @@ -239,42 +234,6 @@ func (s *EEBUSService) LocalDevice() *spine.DeviceLocalImpl { return s.spineLocalDevice } -// return the local entity 1 -func (s *EEBUSService) LocalEntity() *spine.EntityLocalImpl { - return s.spineLocalDevice.Entity([]model.AddressEntityType{1}) -} - -// Add a new entity, used for connected EVs -// Only for EVSE implementations -func (s *EEBUSService) AddEntity(entity *spine.EntityLocalImpl) { - s.spineLocalDevice.AddEntity(entity) -} - -// Remove an entity, used for disconnected EVs -// Only for EVSE implementations -func (s *EEBUSService) RemoveEntity(entity *spine.EntityLocalImpl) { - s.spineLocalDevice.RemoveEntity(entity) -} - -// return all remote devices -func (s *EEBUSService) RemoteDevices() []*spine.DeviceRemoteImpl { - return s.spineLocalDevice.RemoteDevices() -} - -func (s *EEBUSService) RemoteDeviceForSki(ski string) *spine.DeviceRemoteImpl { - return s.spineLocalDevice.RemoteDeviceForSki(ski) -} - -// return a specific remote device of a given DeviceType -func (s *EEBUSService) RemoteDeviceOfType(deviceType model.DeviceTypeType) *spine.DeviceRemoteImpl { - for _, device := range s.spineLocalDevice.RemoteDevices() { - if *device.DeviceType() == deviceType { - return device - } - } - return nil -} - // Returns the Service detail of a given remote SKI func (s *EEBUSService) RemoteServiceForSKI(ski string) *ServiceDetails { return s.connectionsHub.serviceForSKI(ski) diff --git a/service/types.go b/service/types.go index d3340652..ee710041 100644 --- a/service/types.go +++ b/service/types.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "errors" "fmt" + "time" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -113,6 +114,10 @@ type Configuration struct { // SPINE Protocol Specification 6 featureSet model.NetworkManagementFeatureSetType + // SPINE entity types for each entity that should be created + // Each entity has to have a different type! + entityTypes []model.EntityTypeType + // Network interface to use for the service // Optional, if not set all detected interfaces will be used interfaces []string @@ -136,6 +141,9 @@ type Configuration struct { // This is useful when e.g. power values are not available and therefor // need to be calculated using the current values voltage float64 + + // The timeout to be used for sending heartbeats + heartbeatTimeout time.Duration } // Setup a Configuration with the required parameters @@ -145,14 +153,17 @@ func NewConfiguration( deviceModel, serialNumber string, deviceType model.DeviceTypeType, + entityTypes []model.EntityTypeType, port int, certificate tls.Certificate, voltage float64, + heartbeatTimeout time.Duration, ) (*Configuration, error) { configuration := &Configuration{ - certificate: certificate, - port: port, - voltage: voltage, + certificate: certificate, + port: port, + voltage: voltage, + heartbeatTimeout: heartbeatTimeout, } isRequired := "is required" @@ -182,7 +193,11 @@ func NewConfiguration( } else { configuration.deviceType = deviceType } - + if len(entityTypes) == 0 { + return nil, fmt.Errorf("entityTypes %s", isRequired) + } else { + configuration.entityTypes = entityTypes + } // set default configuration.featureSet = model.NetworkManagementFeatureSetTypeSmart diff --git a/ship/connection_test.go b/ship/connection_test.go index d87201cd..ddbf6d02 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -3,6 +3,7 @@ package ship import ( "encoding/json" "testing" + "time" "github.com/enbility/eebus-go/ship/model" "github.com/enbility/eebus-go/spine" @@ -33,7 +34,7 @@ func (s *ConnectionSuite) TearDownTest() {} func (s *ConnectionSuite) BeforeTest(suiteName, testName string) { s.sentMessage = nil localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart) + "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart, time.Second*4) ctrl := gomock.NewController(s.T()) diff --git a/ship/hs_helper_test.go b/ship/hs_helper_test.go index 3a986725..510f9180 100644 --- a/ship/hs_helper_test.go +++ b/ship/hs_helper_test.go @@ -4,6 +4,7 @@ import ( "os" "sync" "testing" + "time" "github.com/enbility/eebus-go/spine" spineModel "github.com/enbility/eebus-go/spine/model" @@ -52,7 +53,7 @@ func (s *dataHandlerTest) HandleShipHandshakeStateUpdate(string, ShipState) {} func initTest(role shipRole) (*ShipConnection, *dataHandlerTest) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart) + "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart, time.Second*4) dataHandler := &dataHandlerTest{} conhandler := NewConnectionHandler(dataHandler, dataHandler, localDevice, role, "LocalShipID", "RemoveDevice", "RemoteShipID") diff --git a/spine/binding_manager.go b/spine/binding_manager.go index e9f141e6..7f3977fd 100644 --- a/spine/binding_manager.go +++ b/spine/binding_manager.go @@ -4,14 +4,16 @@ import ( "errors" "fmt" "reflect" + "sync" "sync/atomic" "github.com/ahmetb/go-linq/v3" "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" ) type BindingManager interface { - AddBinding(localDevice *DeviceLocalImpl, remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error + AddBinding(remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry @@ -24,25 +26,30 @@ type BindingEntry struct { } type BindingManagerImpl struct { + localDevice *DeviceLocalImpl + bindingNum uint64 bindingEntries []*BindingEntry + + mux sync.Mutex // TODO: add persistence } -func NewBindingManager() BindingManager { +func NewBindingManager(localDevice *DeviceLocalImpl) BindingManager { c := &BindingManagerImpl{ - bindingNum: 0, + bindingNum: 0, + localDevice: localDevice, } return c } // is sent from the client (remote device) to the server (local device) -func (c *BindingManagerImpl) AddBinding(localDevice *DeviceLocalImpl, remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error { +func (c *BindingManagerImpl) AddBinding(remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error { - serverFeature := localDevice.FeatureByAddress(data.ServerAddress) + serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) if serverFeature == nil { - return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *localDevice.Address()) + return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *c.localDevice.Address()) } if err := c.checkRoleAndType(serverFeature, model.RoleTypeServer, *data.ServerFeatureType); err != nil { return err @@ -62,7 +69,15 @@ func (c *BindingManagerImpl) AddBinding(localDevice *DeviceLocalImpl, remoteDevi clientFeature: clientFeature, } - // TOV-TODO: check if binding already exists + c.mux.Lock() + defer c.mux.Unlock() + + for _, item := range c.bindingEntries { + if reflect.DeepEqual(item.serverFeature, serverFeature) && reflect.DeepEqual(item.clientFeature, clientFeature) { + return fmt.Errorf("requested binding is already present") + } + } + c.bindingEntries = append(c.bindingEntries, bindingEntry) payload := EventPayload{ @@ -74,14 +89,10 @@ func (c *BindingManagerImpl) AddBinding(localDevice *DeviceLocalImpl, remoteDevi } Events.Publish(payload) - // TOV-TODO: Send heartbeat to the feature which subscribed to DeviceDiagnostic - return nil } func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error { - // TODO: test this!!! - var newBindingEntries []*BindingEntry // according to the spec 7.4.4 @@ -90,13 +101,30 @@ func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCal // b. The absence of "bindingDelete. serverAddress. device" SHALL be treated as if it was // present and set to the recipient's "device" address part. - clientAddress := data.ClientAddress + var clientAddress model.FeatureAddressType + util.DeepCopy(data.ClientAddress, &clientAddress) if data.ClientAddress.Device == nil { clientAddress.Device = remoteDevice.Address() } + clientFeature := remoteDevice.FeatureByAddress(data.ClientAddress) + if clientFeature == nil { + return fmt.Errorf("client feature '%s' in remote device '%s' not found", data.ClientAddress, *remoteDevice.Address()) + } + + serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) + if serverFeature == nil { + return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *c.localDevice.Address()) + } + + c.mux.Lock() + defer c.mux.Unlock() + for _, item := range c.bindingEntries { - if !reflect.DeepEqual(item.clientFeature.Address(), clientAddress) { + itemAddress := item.clientFeature.Address() + + if !reflect.DeepEqual(*itemAddress, clientAddress) && + !reflect.DeepEqual(item.serverFeature, serverFeature) { newBindingEntries = append(newBindingEntries, item) } } @@ -107,13 +135,25 @@ func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCal c.bindingEntries = newBindingEntries - // TOV-TODO: stop heartbeat for remote device when it has no binding to DeviceDiagnostic anymore + payload := EventPayload{ + Ski: remoteDevice.ski, + EventType: EventTypeBindingChange, + ChangeType: ElementChangeRemove, + Data: data, + Device: remoteDevice, + Feature: clientFeature, + } + Events.Publish(payload) + return nil } func (c *BindingManagerImpl) Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry { var result []*BindingEntry + c.mux.Lock() + defer c.mux.Unlock() + linq.From(c.bindingEntries).WhereT(func(s *BindingEntry) bool { return s.clientFeature.Device().Ski() == remoteDevice.Ski() }).ToSlice(&result) @@ -124,6 +164,9 @@ func (c *BindingManagerImpl) Bindings(remoteDevice *DeviceRemoteImpl) []*Binding func (c *BindingManagerImpl) BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry { var result []*BindingEntry + c.mux.Lock() + defer c.mux.Unlock() + linq.From(c.bindingEntries).WhereT(func(s *BindingEntry) bool { return reflect.DeepEqual(*s.serverFeature.Address(), featureAddress) }).ToSlice(&result) diff --git a/spine/binding_manager_test.go b/spine/binding_manager_test.go new file mode 100644 index 00000000..beceaa91 --- /dev/null +++ b/spine/binding_manager_test.go @@ -0,0 +1,82 @@ +package spine_test + +import ( + "testing" + "time" + + "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestBindingManagerSuite(t *testing.T) { + suite.Run(t, new(BindingManagerSuite)) +} + +type BindingManagerSuite struct { + suite.Suite + + localDevice *spine.DeviceLocalImpl + remoteDevice *spine.DeviceRemoteImpl + sut spine.BindingManager +} + +func (suite *BindingManagerSuite) WriteSpineMessage([]byte) {} + +func (suite *BindingManagerSuite) SetupSuite() { + suite.localDevice = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + + ski := "test" + sender := spine.NewSender(suite) + suite.remoteDevice = spine.NewDeviceRemoteImpl(suite.localDevice, ski, sender) + + suite.localDevice.AddRemoteDevice(ski, suite) + + suite.sut = spine.NewBindingManager(suite.localDevice) +} + +func (suite *BindingManagerSuite) Test_Bindings() { + entity := spine.NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) + suite.localDevice.AddEntity(entity) + + localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + + remoteEntity := spine.NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + suite.remoteDevice.AddEntity(remoteEntity) + + remoteFeature := spine.NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + remoteFeature.Address().Device = util.Ptr(model.AddressDeviceType("remoteDevice")) + remoteEntity.AddFeature(remoteFeature) + + bindingRequest := model.BindingManagementRequestCallType{ + ClientAddress: remoteFeature.Address(), + ServerAddress: localFeature.Address(), + ServerFeatureType: util.Ptr(model.FeatureTypeTypeDeviceDiagnosis), + } + + bindingMgr := suite.localDevice.BindingManager() + err := bindingMgr.AddBinding(suite.remoteDevice, bindingRequest) + assert.Nil(suite.T(), err) + + err = bindingMgr.AddBinding(suite.remoteDevice, bindingRequest) + assert.NotNil(suite.T(), err) + + subs := bindingMgr.Bindings(suite.remoteDevice) + assert.Equal(suite.T(), 1, len(subs)) + + bindingDelete := model.BindingManagementDeleteCallType{ + ClientAddress: remoteFeature.Address(), + ServerAddress: localFeature.Address(), + } + + err = bindingMgr.RemoveBinding(bindingDelete, suite.remoteDevice) + assert.Nil(suite.T(), err) + + subs = bindingMgr.Bindings(suite.remoteDevice) + assert.Equal(suite.T(), 0, len(subs)) + + err = bindingMgr.RemoveBinding(bindingDelete, suite.remoteDevice) + assert.NotNil(suite.T(), err) +} diff --git a/spine/device_local.go b/spine/device_local.go index be1ff5f7..bced83a1 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -5,6 +5,7 @@ import ( "fmt" "reflect" "sync" + "time" "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/spine/model" @@ -22,6 +23,7 @@ type DeviceLocalImpl struct { entities []*EntityLocalImpl subscriptionManager SubscriptionManager bindingManager BindingManager + heartbeatManager HeartbeatManager nodeManagement *NodeManagementImpl remoteDevices map[string]*DeviceRemoteImpl @@ -39,7 +41,7 @@ type DeviceLocalImpl struct { // SerialNumber is the serial number // DeviceCode is the SHIP id (accessMethods.id) // DeviceAddress is the SPINE device address -func NewDeviceLocalImpl(brandName, deviceModel, serialNumber, deviceCode, deviceAddress string, deviceType model.DeviceTypeType, featureSet model.NetworkManagementFeatureSetType) *DeviceLocalImpl { +func NewDeviceLocalImpl(brandName, deviceModel, serialNumber, deviceCode, deviceAddress string, deviceType model.DeviceTypeType, featureSet model.NetworkManagementFeatureSetType, heartbeatTimeout time.Duration) *DeviceLocalImpl { address := model.AddressDeviceType(deviceAddress) var fSet *model.NetworkManagementFeatureSetType @@ -48,16 +50,18 @@ func NewDeviceLocalImpl(brandName, deviceModel, serialNumber, deviceCode, device } res := &DeviceLocalImpl{ - DeviceImpl: NewDeviceImpl(&address, &deviceType, fSet), - subscriptionManager: NewSubscriptionManager(), - bindingManager: NewBindingManager(), - remoteDevices: make(map[string]*DeviceRemoteImpl), - brandName: brandName, - deviceModel: deviceModel, - serialNumber: serialNumber, - deviceCode: deviceCode, + DeviceImpl: NewDeviceImpl(&address, &deviceType, fSet), + remoteDevices: make(map[string]*DeviceRemoteImpl), + brandName: brandName, + deviceModel: deviceModel, + serialNumber: serialNumber, + deviceCode: deviceCode, } + res.subscriptionManager = NewSubscriptionManager(res) + res.bindingManager = NewBindingManager(res) + res.heartbeatManager = NewHeartbeatManager(res, res.subscriptionManager, heartbeatTimeout) + res.addDeviceInformation() return res } @@ -88,7 +92,8 @@ func (r *DeviceLocalImpl) AddRemoteDeviceForSki(ski string, rDevice *DeviceRemot // Adds a new remote device with a given SKI and triggers SPINE requesting device details func (r *DeviceLocalImpl) AddRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing { - rDevice := NewDeviceRemoteImpl(r, ski, writeI) + sender := NewSender(writeI) + rDevice := NewDeviceRemoteImpl(r, ski, sender) r.AddRemoteDeviceForSki(ski, rDevice) @@ -107,11 +112,7 @@ func (r *DeviceLocalImpl) AddRemoteDevice(ski string, writeI SpineDataConnection // React to some specific events func (r *DeviceLocalImpl) HandleEvent(payload EventPayload) { // Subscribe to NodeManagment after DetailedDiscovery is received - if payload.EventType != EventTypeDeviceChange { - return - } - - if payload.ChangeType != ElementChangeAdd { + if payload.EventType != EventTypeDeviceChange || payload.ChangeType != ElementChangeAdd { return } @@ -144,7 +145,7 @@ func (r *DeviceLocalImpl) RemoveRemoteDevice(ski string) { return } - r.remoteDevices[ski].CloseConnection() + // TODO: make sure subscriptions are removed, that way heartbeat should also be removed delete(r.remoteDevices, ski) // only unsubscribe if we don't have any remote devices left @@ -237,6 +238,10 @@ func (r *DeviceLocalImpl) ProcessCmd(datagram model.DatagramType, remoteDevice * return nil } +func (r *DeviceLocalImpl) NodeManagement() NodeManagementImpl { + return *r.nodeManagement +} + func (r *DeviceLocalImpl) SubscriptionManager() SubscriptionManager { return r.subscriptionManager } @@ -245,6 +250,10 @@ func (r *DeviceLocalImpl) BindingManager() BindingManager { return r.bindingManager } +func (r *DeviceLocalImpl) HeartbeatManager() HeartbeatManager { + return r.heartbeatManager +} + func (r *DeviceLocalImpl) AddEntity(entity *EntityLocalImpl) { r.entities = append(r.entities, entity) @@ -274,27 +283,20 @@ func (r *DeviceLocalImpl) Entity(id []model.AddressEntityType) *EntityLocalImpl return nil } -func (r *DeviceLocalImpl) FeatureByAddress(address *model.FeatureAddressType) FeatureLocal { - entity := r.Entity(address.Entity) - if entity != nil { - return entity.Feature(address.Feature) +func (r *DeviceLocalImpl) EntityForType(entityType model.EntityTypeType) *EntityLocalImpl { + for _, e := range r.entities { + if e.eType == entityType { + return e + } } return nil } -func (r *DeviceLocalImpl) FeatureByTypeAndRole(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal { - if len(r.entities) < 1 { - return nil - } - - for _, entity := range r.entities { - for _, feature := range entity.Features() { - if feature.Type() == featureType && feature.Role() == role { - return feature - } - } +func (r *DeviceLocalImpl) FeatureByAddress(address *model.FeatureAddressType) FeatureLocal { + entity := r.Entity(address.Entity) + if entity != nil { + return entity.Feature(address.Feature) } - return nil } diff --git a/spine/device_local_test.go b/spine/device_local_test.go index c3b4008d..fcc95d13 100644 --- a/spine/device_local_test.go +++ b/spine/device_local_test.go @@ -2,6 +2,7 @@ package spine_test import ( "testing" + "time" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" @@ -23,7 +24,7 @@ var _ spine.SpineDataConnection = (*DeviceLocalTestSuite)(nil) func (d *DeviceLocalTestSuite) WriteSpineMessage([]byte) {} func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() { - sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) + sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) ski := "test" sut.AddRemoteDevice(ski, d) @@ -37,7 +38,7 @@ func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() { } func (d *DeviceLocalTestSuite) Test_RemoteDevice() { - sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) + sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) sut.AddEntity(localEntity) @@ -76,7 +77,7 @@ func (d *DeviceLocalTestSuite) Test_RemoteDevice() { feature1 := sut.FeatureByAddress(featureAddress) assert.NotNil(d.T(), feature1) - feature2 := sut.FeatureByTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + feature2 := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) assert.NotNil(d.T(), feature2) sut.RemoveEntity(entity1) @@ -89,7 +90,7 @@ func (d *DeviceLocalTestSuite) Test_RemoteDevice() { } func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() { - sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) + sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) sut.AddEntity(localEntity) @@ -140,7 +141,7 @@ func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() { } func (d *DeviceLocalTestSuite) Test_ProcessCmd() { - sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) + sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) sut.AddEntity(localEntity) diff --git a/spine/device_remote.go b/spine/device_remote.go index 03782f25..e2c68681 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -21,21 +21,16 @@ type DeviceRemoteImpl struct { sender Sender localDevice *DeviceLocalImpl - - // Heartbeat Sender - heartbeatSender *HeartbeatSender } var _ SpineDataProcessing = (*DeviceRemoteImpl)(nil) -func NewDeviceRemoteImpl(localDevice *DeviceLocalImpl, ski string, writeHandler SpineDataConnection) *DeviceRemoteImpl { - sender := NewSender(writeHandler) +func NewDeviceRemoteImpl(localDevice *DeviceLocalImpl, ski string, sender Sender) *DeviceRemoteImpl { res := DeviceRemoteImpl{ - DeviceImpl: NewDeviceImpl(nil, nil, nil), - ski: ski, - localDevice: localDevice, - sender: sender, - heartbeatSender: NewHeartbeatSender(sender), + DeviceImpl: NewDeviceImpl(nil, nil, nil), + ski: ski, + localDevice: localDevice, + sender: sender, } res.addNodeManagement() @@ -47,24 +42,9 @@ func (d *DeviceRemoteImpl) Ski() string { return d.ski } -// Needs to be called by the CEM implementation once a subscription for the local DeviceDiagnosis server feature is received -func (d *DeviceRemoteImpl) StartHeartbeatSend(senderAddr, destinationAddr *model.FeatureAddressType) { - d.heartbeatSender.StartHeartbeatSend(senderAddr, destinationAddr) -} - -func (d *DeviceRemoteImpl) IsHeartbeatMsgCounter(msgCounter model.MsgCounterType) bool { - return d.heartbeatSender.IsHeartbeatMsgCounter(msgCounter) -} - -// Needs to be called by the CEM implementation once a subscription for the local DeviceDiagnosis server feature is removed -func (d *DeviceRemoteImpl) Stopheartbeat() { - d.heartbeatSender.StopHeartbeat() -} - -// this connection is closed -func (d *DeviceRemoteImpl) CloseConnection() { - d.heartbeatSender.StopHeartbeat() -} +// // this connection is closed +// func (d *DeviceRemoteImpl) CloseConnection() { +// } // processing incoming SPINE message from the associated SHIP connection func (d *DeviceRemoteImpl) HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) { @@ -242,10 +222,10 @@ func (d *DeviceRemoteImpl) CheckEntityInformation(initialData bool, entity model func (d *DeviceRemoteImpl) addNewEntity(eType model.EntityTypeType, address []model.AddressEntityType) *EntityRemoteImpl { newEntity := NewEntityRemoteImpl(d, eType, address) - return d.addEntity(newEntity) + return d.AddEntity(newEntity) } -func (d *DeviceRemoteImpl) addEntity(entity *EntityRemoteImpl) *EntityRemoteImpl { +func (d *DeviceRemoteImpl) AddEntity(entity *EntityRemoteImpl) *EntityRemoteImpl { d.entitiesMutex.Lock() defer d.entitiesMutex.Unlock() diff --git a/spine/entity_local_test.go b/spine/entity_local_test.go new file mode 100644 index 00000000..11c40776 --- /dev/null +++ b/spine/entity_local_test.go @@ -0,0 +1,57 @@ +package spine_test + +import ( + "testing" + "time" + + "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestEntityLocalSuite(t *testing.T) { + suite.Run(t, new(EntityLocalTestSuite)) +} + +type EntityLocalTestSuite struct { + suite.Suite +} + +func (suite *EntityLocalTestSuite) Test_Entity() { + device := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + entity := spine.NewEntityLocalImpl(device, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + device.AddEntity(entity) + + f := spine.NewFeatureLocalImpl(1, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + entity.AddFeature(f) + assert.Equal(suite.T(), 1, len(entity.Features())) + + entity.AddFeature(f) + assert.Equal(suite.T(), 1, len(entity.Features())) + + f1 := entity.Feature(nil) + assert.Nil(suite.T(), f1) + + f1 = entity.Feature(f.Address().Feature) + assert.NotNil(suite.T(), f1) + + fakeAddress := model.AddressFeatureType(5) + f1 = entity.Feature(&fakeAddress) + assert.Nil(suite.T(), f1) + + f2 := entity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + assert.NotNil(suite.T(), f2) + + assert.Equal(suite.T(), 2, len(entity.Features())) + + f3 := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + assert.NotNil(suite.T(), f3) + + assert.Equal(suite.T(), 3, len(entity.Features())) + + f4 := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + assert.NotNil(suite.T(), f4) + + assert.Equal(suite.T(), 3, len(entity.Features())) +} diff --git a/spine/feature_local_test.go b/spine/feature_local_test.go index a7296b3e..df2fc4b6 100644 --- a/spine/feature_local_test.go +++ b/spine/feature_local_test.go @@ -2,6 +2,7 @@ package spine_test import ( "testing" + "time" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/mocks" @@ -32,8 +33,8 @@ func (suite *DeviceClassificationTestSuite) SetupSuite() { suite.featureType = model.FeatureTypeTypeDeviceClassification suite.msgCounter = model.MsgCounterType(1) - suite.remoteFeature = spine.CreateRemoteDeviceAndFeature(1, suite.featureType, model.RoleTypeServer, suite.senderMock) - suite.sut = CreateLocalDeviceAndFeature(1, suite.featureType, model.RoleTypeClient) + suite.remoteFeature = createRemoteDeviceAndFeature(1, suite.featureType, model.RoleTypeServer, suite.senderMock) + suite.sut = createLocalDeviceAndFeature(1, suite.featureType, model.RoleTypeClient) } func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Reply() { @@ -125,8 +126,8 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Err assert.Equal(suite.T(), errorDescription, string(*err.Description)) } -func CreateLocalDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType) *spine.FeatureLocalImpl { - localDevice := spine.NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) +func createLocalDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType) *spine.FeatureLocalImpl { + localDevice := spine.NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) localDevice.AddEntity(localEntity) localFeature := spine.NewFeatureLocalImpl(localEntity.NextFeatureId(), localEntity, featureType, role) diff --git a/spine/feature_remote_test.go b/spine/feature_remote_test.go index fe006085..a1bbdfd0 100644 --- a/spine/feature_remote_test.go +++ b/spine/feature_remote_test.go @@ -1,19 +1,20 @@ -package spine +package spine_test import ( + "time" + + "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" ) -func CreateRemoteDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType, sender Sender) *FeatureRemoteImpl { - localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) +func createRemoteDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType, sender spine.Sender) *spine.FeatureRemoteImpl { + localDevice := spine.NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - remoteDevice := NewDeviceRemoteImpl(localDevice, "ski", nil) - remoteDevice.address = util.Ptr(model.AddressDeviceType("Address")) - remoteDevice.sender = sender - remoteEntity := NewEntityRemoteImpl(remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) - remoteDevice.addEntity(remoteEntity) - remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, role) + remoteDevice := spine.NewDeviceRemoteImpl(localDevice, "ski", sender) + // remoteDevice.address = util.Ptr(model.AddressDeviceType("Address")) + remoteEntity := spine.NewEntityRemoteImpl(remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) + remoteDevice.AddEntity(remoteEntity) + remoteFeature := spine.NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, role) remoteEntity.AddFeature(remoteFeature) return remoteFeature } diff --git a/spine/heartbeat.go b/spine/heartbeat.go deleted file mode 100644 index b7672d0f..00000000 --- a/spine/heartbeat.go +++ /dev/null @@ -1,148 +0,0 @@ -package spine - -import ( - "sync" - "sync/atomic" - "time" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine/model" -) - -const hearbeatMsgCountersSize = 10 - -type HeartbeatSender struct { - heartBeatNum uint64 // see https://github.com/golang/go/issues/11891 - stopHeartbeatC chan struct{} - stopMux sync.Mutex - senderAddr, destinationAddr *model.FeatureAddressType - sender Sender - heartBeatTimeout *model.DurationType - - msgCounters []model.MsgCounterType - - mux sync.Mutex -} - -func NewHeartbeatSender(sender Sender) *HeartbeatSender { - h := &HeartbeatSender{ - sender: sender, - } - // default to 4 seconds timeout - h.heartBeatTimeout = model.NewDurationType(time.Second * 4) - - return h -} - -func (c *HeartbeatSender) AddMsgCounter(msgCounter *model.MsgCounterType) { - if msgCounter == nil { - return - } - - c.mux.Lock() - defer c.mux.Unlock() - - c.msgCounters = append(c.msgCounters, *msgCounter) - if len(c.msgCounters) > hearbeatMsgCountersSize { - c.msgCounters = c.msgCounters[1:] - } -} - -func (c *HeartbeatSender) IsHeartbeatMsgCounter(msgCounter model.MsgCounterType) bool { - c.mux.Lock() - defer c.mux.Unlock() - - for _, item := range c.msgCounters { - if item == msgCounter { - return true - } - } - - return false -} - -func (c *HeartbeatSender) StartHeartbeatSend(senderAddr, destinationAddr *model.FeatureAddressType) { - // stop a already running heartbeat - c.StopHeartbeat() - - c.senderAddr = senderAddr - c.destinationAddr = destinationAddr - - c.stopHeartbeatC = make(chan struct{}) - - go func() { - c.sendHearbeat(c.stopHeartbeatC, 800*time.Millisecond) - }() -} - -func (c *HeartbeatSender) StopHeartbeat() { - c.stopMux.Lock() - defer c.stopMux.Unlock() - - if c.stopHeartbeatC != nil && !c.isHeartbeatClosed() { - close(c.stopHeartbeatC) - } -} - -func (c *HeartbeatSender) heartbeatCmd(t time.Time) model.CmdType { - timestamp := t.UTC().Format(time.RFC3339) - cmd := model.CmdType{ - DeviceDiagnosisHeartbeatData: &model.DeviceDiagnosisHeartbeatDataType{ - Timestamp: ×tamp, - HeartbeatCounter: c.heartBeatCounter(), - HeartbeatTimeout: c.heartBeatTimeout, - }, - } - - return cmd -} - -func (c *HeartbeatSender) SendHeartBeatData(requestHeader *model.HeaderType) error { - // TODO is this all we need here? - - cmd := c.heartbeatCmd(time.Now()) - - return c.sender.Reply(requestHeader, c.senderAddr, cmd) -} - -func (c *HeartbeatSender) sendHearbeat(stopC chan struct{}, d time.Duration) { - ticker := time.NewTicker(d) - for { - select { - case <-ticker.C: - - if c.senderAddr == nil || c.destinationAddr == nil { - break - } - - cmd := c.heartbeatCmd(time.Now()) - - msgCounter, err := c.sender.Notify(c.senderAddr, c.destinationAddr, cmd) - if err != nil { - logging.Log.Debug("ERROR sending heartbeat: ", err) - } - if msgCounter != nil { - c.msgCounters = append(c.msgCounters, *msgCounter) - } - - case <-stopC: - return - } - } -} - -func (c *HeartbeatSender) isHeartbeatClosed() bool { - select { - case <-c.stopHeartbeatC: - return true - default: - } - - return false -} - -// TODO heartBeatCounter should be global on CEM level, not on connection level -func (c *HeartbeatSender) heartBeatCounter() *uint64 { - i := atomic.AddUint64(&c.heartBeatNum, 1) - return &i -} diff --git a/spine/heartbeat_manager.go b/spine/heartbeat_manager.go new file mode 100644 index 00000000..ed5219a1 --- /dev/null +++ b/spine/heartbeat_manager.go @@ -0,0 +1,169 @@ +package spine + +import ( + "errors" + "sync" + "sync/atomic" + "time" + + "github.com/enbility/eebus-go/spine/model" +) + +type HeartbeatManager interface { + IsHeartbeatRunning() bool + UpdateHeartbeatOnSubscriptions() + StartHeartbeat() error + StopHeartbeat() +} + +type HeartbeatManagerImpl struct { + localDevice *DeviceLocalImpl + localEntity *EntityLocalImpl + localFeature FeatureLocal + + heartBeatNum uint64 // see https://github.com/golang/go/issues/11891 + stopHeartbeatC chan struct{} + stopMux sync.Mutex + + subscriptionManager SubscriptionManager + heartBeatTimeout *model.DurationType +} + +// Create a new Heartbeat Manager which handles sending of heartbeats +func NewHeartbeatManager(localDevice *DeviceLocalImpl, subscriptionManager SubscriptionManager, timeout time.Duration) HeartbeatManager { + h := &HeartbeatManagerImpl{ + localDevice: localDevice, + subscriptionManager: subscriptionManager, + heartBeatTimeout: model.NewDurationType(timeout), + } + + return h +} + +func (c *HeartbeatManagerImpl) IsHeartbeatRunning() bool { + c.stopMux.Lock() + defer c.stopMux.Unlock() + + if c.stopHeartbeatC != nil && !c.isHeartbeatClosed() { + return true + } + + return false +} + +// check if there are any heartbeat subscriptions left, otherwise stop creating new ones +// or start creating heartbeats again if needed +func (c *HeartbeatManagerImpl) UpdateHeartbeatOnSubscriptions() { + if err := c.updateLocal(); err != nil { + return + } + + featureAddr := c.localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + if featureAddr == nil { + return + } + + subscriptions := c.subscriptionManager.SubscriptionsOnFeature(*featureAddr.Address()) + if len(subscriptions) == 0 { + // stop creating heartbeats + c.StopHeartbeat() + } else if !c.IsHeartbeatRunning() { + // resume creating heartbeats + _ = c.StartHeartbeat() + } +} + +func (c *HeartbeatManagerImpl) updateLocal() error { + if c.localFeature == nil { + // search through all entities on the device + entities := c.localDevice.Entities() + for _, entity := range entities { + localFeature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + if localFeature != nil { + c.localFeature = localFeature + c.localEntity = entity + } + } + + if c.localFeature == nil { + return errors.New("Local feature for heartbeat sender address could not be found") + } + } + + return nil +} + +// Start setting heartbeat data +// Make sure the a required FeatureTypeTypeDeviceDiagnosis with the role server is present +// otherwise this will end with an error +// Note: Remote features need to have a subscription to get notifications +func (c *HeartbeatManagerImpl) StartHeartbeat() error { + if err := c.updateLocal(); err != nil { + return err + } + + timeout, err := c.heartBeatTimeout.GetTimeDuration() + if err != nil { + return err + } + + // stop an already running heartbeat + c.StopHeartbeat() + + c.stopHeartbeatC = make(chan struct{}) + + go c.updateHearbeatData(c.stopHeartbeatC, timeout) + + return nil +} + +// Stop updating heartbeat data +// Note: No active subscribers will get any further notifications! +func (c *HeartbeatManagerImpl) StopHeartbeat() { + if c.IsHeartbeatRunning() { + close(c.stopHeartbeatC) + } +} + +func (c *HeartbeatManagerImpl) heartbeatData(t time.Time, counter *uint64) *model.DeviceDiagnosisHeartbeatDataType { + timestamp := t.UTC().Format(time.RFC3339) + + return &model.DeviceDiagnosisHeartbeatDataType{ + Timestamp: ×tamp, + HeartbeatCounter: counter, + HeartbeatTimeout: c.heartBeatTimeout, + } +} + +func (c *HeartbeatManagerImpl) updateHearbeatData(stopC chan struct{}, d time.Duration) { + ticker := time.NewTicker(d) + for { + select { + case <-ticker.C: + + heartbeatData := c.heartbeatData(time.Now(), c.heartBeatCounter()) + + // updating the data will automatically notify all subscribed remote features + c.localFeature.SetData(model.FunctionTypeDeviceDiagnosisHeartbeatData, heartbeatData) + + case <-stopC: + return + } + } +} + +func (c *HeartbeatManagerImpl) isHeartbeatClosed() bool { + select { + case <-c.stopHeartbeatC: + return true + default: + } + + return false +} + +// TODO heartBeatCounter should be global on CEM level, not on connection level +func (c *HeartbeatManagerImpl) heartBeatCounter() *uint64 { + i := atomic.AddUint64(&c.heartBeatNum, 1) + return &i +} diff --git a/spine/heartbeat_manager_test.go b/spine/heartbeat_manager_test.go new file mode 100644 index 00000000..5afc1eb8 --- /dev/null +++ b/spine/heartbeat_manager_test.go @@ -0,0 +1,139 @@ +package spine_test + +import ( + "testing" + "time" + + "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestHeartbeatManagerSuite(t *testing.T) { + suite.Run(t, new(HeartBeatManagerSuite)) +} + +type HeartBeatManagerSuite struct { + suite.Suite + + localDevice *spine.DeviceLocalImpl + remoteDevice *spine.DeviceRemoteImpl + sut spine.HeartbeatManager +} + +func (suite *HeartBeatManagerSuite) WriteSpineMessage([]byte) {} + +func (suite *HeartBeatManagerSuite) SetupSuite() { + suite.localDevice = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + + ski := "test" + sender := spine.NewSender(suite) + suite.remoteDevice = spine.NewDeviceRemoteImpl(suite.localDevice, ski, sender) + + suite.localDevice.AddRemoteDevice(ski, suite) + + suite.sut = suite.localDevice.HeartbeatManager() +} + +func (suite *HeartBeatManagerSuite) Test_HeartbeatFailure() { + err := suite.sut.StartHeartbeat() + assert.NotNil(suite.T(), err) +} + +func (suite *HeartBeatManagerSuite) Test_HeartbeatSuccess() { + entity := spine.NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) + suite.localDevice.AddEntity(entity) + + localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + entity.AddFeature(localFeature) + + remoteEntity := spine.NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + suite.remoteDevice.AddEntity(remoteEntity) + + remoteFeature := spine.NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + remoteEntity.AddFeature(remoteFeature) + + subscrRequest := &model.SubscriptionManagementRequestCallType{ + ClientAddress: remoteFeature.Address(), + ServerAddress: localFeature.Address(), + ServerFeatureType: util.Ptr(model.FeatureTypeTypeDeviceDiagnosis), + } + + datagram := model.DatagramType{ + Header: model.HeaderType{ + SpecificationVersion: &spine.SpecificationVersion, + AddressSource: &model.FeatureAddressType{ + Device: suite.remoteDevice.Address(), + Entity: []model.AddressEntityType{0}, + Feature: util.Ptr(model.AddressFeatureType(0)), + }, + AddressDestination: &model.FeatureAddressType{ + Device: suite.localDevice.Address(), + Entity: []model.AddressEntityType{0}, + Feature: util.Ptr(model.AddressFeatureType(0)), + }, + MsgCounter: util.Ptr(model.MsgCounterType(1000)), + CmdClassifier: util.Ptr(model.CmdClassifierTypeCall), + }, + Payload: model.PayloadType{ + Cmd: []model.CmdType{ + { + NodeManagementSubscriptionRequestCall: &model.NodeManagementSubscriptionRequestCallType{ + SubscriptionRequest: subscrRequest, + }, + }, + }, + }, + } + err := suite.localDevice.ProcessCmd(datagram, suite.remoteDevice) + assert.Nil(suite.T(), err) + + data := localFeature.Data(model.FunctionTypeDeviceDiagnosisHeartbeatData) + assert.Nil(suite.T(), data) + + running := suite.sut.IsHeartbeatRunning() + assert.Equal(suite.T(), true, running) + + err = suite.sut.StartHeartbeat() + assert.Nil(suite.T(), err) + + time.Sleep(time.Second * 5) + + running = suite.sut.IsHeartbeatRunning() + assert.Equal(suite.T(), true, running) + + data = localFeature.Data(model.FunctionTypeDeviceDiagnosisHeartbeatData) + assert.NotNil(suite.T(), data) + + fctData := data.(*model.DeviceDiagnosisHeartbeatDataType) + var resultCounter uint64 = 1 + assert.Equal(suite.T(), resultCounter, *fctData.HeartbeatCounter) + resultTimeout, err := fctData.HeartbeatTimeout.GetTimeDuration() + assert.Nil(suite.T(), err) + assert.Equal(suite.T(), time.Second*4, resultTimeout) + + subscrDelRequest := &model.SubscriptionManagementDeleteCallType{ + ClientAddress: remoteFeature.Address(), + ServerAddress: localFeature.Address(), + } + + datagram.Payload = model.PayloadType{ + Cmd: []model.CmdType{ + { + NodeManagementSubscriptionDeleteCall: &model.NodeManagementSubscriptionDeleteCallType{ + SubscriptionDelete: subscrDelRequest, + }, + }, + }, + } + + err = suite.localDevice.ProcessCmd(datagram, suite.remoteDevice) + assert.Nil(suite.T(), err) + + isHeartbeatRunning := suite.sut.IsHeartbeatRunning() + assert.Equal(suite.T(), false, isHeartbeatRunning) + + suite.sut.StopHeartbeat() +} diff --git a/spine/mocks/Sender.go b/spine/mocks/Sender.go index a04d19ac..af686605 100644 --- a/spine/mocks/Sender.go +++ b/spine/mocks/Sender.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. +// Code generated by mockery v2.36.1. DO NOT EDIT. package mocks @@ -40,6 +40,30 @@ func (_m *Sender) Bind(senderAddress *model.FeatureAddressType, destinationAddre return r0, r1 } +// DatagramForMsgCounter provides a mock function with given fields: msgCounter +func (_m *Sender) DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) { + ret := _m.Called(msgCounter) + + var r0 model.DatagramType + var r1 error + if rf, ok := ret.Get(0).(func(model.MsgCounterType) (model.DatagramType, error)); ok { + return rf(msgCounter) + } + if rf, ok := ret.Get(0).(func(model.MsgCounterType) model.DatagramType); ok { + r0 = rf(msgCounter) + } else { + r0 = ret.Get(0).(model.DatagramType) + } + + if rf, ok := ret.Get(1).(func(model.MsgCounterType) error); ok { + r1 = rf(msgCounter) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Notify provides a mock function with given fields: senderAddress, destinationAddress, cmd func (_m *Sender) Notify(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) { ret := _m.Called(senderAddress, destinationAddress, cmd) @@ -186,13 +210,12 @@ func (_m *Sender) Write(senderAddress *model.FeatureAddressType, destinationAddr return r0, r1 } -type mockConstructorTestingTNewSender interface { +// NewSender creates a new instance of Sender. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSender(t interface { mock.TestingT Cleanup(func()) -} - -// NewSender creates a new instance of Sender. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSender(t mockConstructorTestingTNewSender) *Sender { +}) *Sender { mock := &Sender{} mock.Mock.Test(t) diff --git a/spine/nodemanagement_binding.go b/spine/nodemanagement_binding.go index c8633a03..484734b2 100644 --- a/spine/nodemanagement_binding.go +++ b/spine/nodemanagement_binding.go @@ -53,7 +53,7 @@ func (r *NodeManagementImpl) handleMsgBindingData(message *Message) error { func (r *NodeManagementImpl) handleMsgBindingRequestCall(message *Message, data *model.NodeManagementBindingRequestCallType) error { switch message.CmdClassifier { case model.CmdClassifierTypeCall: - return r.Device().BindingManager().AddBinding(r.Device(), message.FeatureRemote.Device(), *data.BindingRequest) + return r.Device().BindingManager().AddBinding(message.FeatureRemote.Device(), *data.BindingRequest) default: return fmt.Errorf("nodemanagement.handleBindingRequestCall: NodeManagementBindingRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) diff --git a/spine/nodemanagement_defaileddiscovery.go b/spine/nodemanagement_defaileddiscovery.go index 98ad1567..ce3f61e0 100644 --- a/spine/nodemanagement_defaileddiscovery.go +++ b/spine/nodemanagement_defaileddiscovery.go @@ -193,8 +193,12 @@ func (r *NodeManagementImpl) processNotifyDetailedDiscoveryData(message *Message } Events.Publish(payload) + subscriptionMgr := r.Device().SubscriptionManager() // remove all subscriptions for this entity - r.Device().SubscriptionManager().RemoveSubscriptionsForEntity(removedEntity) + subscriptionMgr.RemoveSubscriptionsForEntity(removedEntity) + + // make sure Heartbeat Manager is up to date + r.Device().HeartbeatManager().UpdateHeartbeatOnSubscriptions() } } diff --git a/spine/nodemanagement_subscription.go b/spine/nodemanagement_subscription.go index 2f5631c7..5d215cd9 100644 --- a/spine/nodemanagement_subscription.go +++ b/spine/nodemanagement_subscription.go @@ -53,7 +53,14 @@ func (r *NodeManagementImpl) handleMsgSubscriptionData(message *Message) error { func (r *NodeManagementImpl) handleMsgSubscriptionRequestCall(message *Message, data *model.NodeManagementSubscriptionRequestCallType) error { switch message.CmdClassifier { case model.CmdClassifierTypeCall: - return r.Device().SubscriptionManager().AddSubscription(r.Device(), message.FeatureRemote.Device(), *data.SubscriptionRequest) + subscriptionMgr := r.Device().SubscriptionManager() + + err := subscriptionMgr.AddSubscription(message.FeatureRemote.Device(), *data.SubscriptionRequest) + if err == nil { + r.Device().HeartbeatManager().UpdateHeartbeatOnSubscriptions() + } + + return err default: return fmt.Errorf("nodemanagement.handleSubscriptionRequestCall: NodeManagementSubscriptionRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) @@ -63,8 +70,14 @@ func (r *NodeManagementImpl) handleMsgSubscriptionRequestCall(message *Message, func (r *NodeManagementImpl) handleMsgSubscriptionDeleteCall(message *Message, data *model.NodeManagementSubscriptionDeleteCallType) error { switch message.CmdClassifier { case model.CmdClassifierTypeCall: - return r.Device().SubscriptionManager().RemoveSubscription(*data.SubscriptionDelete, message.FeatureRemote.Device()) + subscriptionMgr := r.Device().SubscriptionManager() + + err := subscriptionMgr.RemoveSubscription(*data.SubscriptionDelete, message.FeatureRemote.Device()) + if err == nil { + r.Device().HeartbeatManager().UpdateHeartbeatOnSubscriptions() + } + return err default: return fmt.Errorf("nodemanagement.handleSubscriptionDeleteCall: NodeManagementSubscriptionRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) } diff --git a/spine/nodemanagement_test.go b/spine/nodemanagement_test.go index 1ecf33ba..148f83a8 100644 --- a/spine/nodemanagement_test.go +++ b/spine/nodemanagement_test.go @@ -24,8 +24,8 @@ func TestNodemanagement_SubscriptionRequestCall(t *testing.T) { //localDevice := NewDeviceLocalImpl(model.AddressDeviceType("server")) - serverFeature := CreateLocalDeviceAndFeature(subscriptionEntityId, featureType, model.RoleTypeServer) - clientFeature := spine.CreateRemoteDeviceAndFeature(subscriptionEntityId, featureType, model.RoleTypeClient, senderMock) + serverFeature := createLocalDeviceAndFeature(subscriptionEntityId, featureType, model.RoleTypeServer) + clientFeature := createRemoteDeviceAndFeature(subscriptionEntityId, featureType, model.RoleTypeClient, senderMock) // serverAddress := featureAddress(serverName, subscriptionEntityId, subscriptionFeatureId) // clientAddress := featureAddress(clientName, subscriptionEntityId, subscriptionFeatureId) diff --git a/spine/sender.go b/spine/send.go similarity index 89% rename from spine/sender.go rename to spine/send.go index 9d0b4c0f..29039b87 100644 --- a/spine/sender.go +++ b/spine/send.go @@ -1,4 +1,3 @@ -//go:generate mockery --name=Sender package spine import ( @@ -9,6 +8,7 @@ import ( "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + lru "github.com/hashicorp/golang-lru/v2" ) type ComControl interface { @@ -16,6 +16,8 @@ type ComControl interface { SendSpineMessage(datagram model.DatagramType) error } +//go:generate mockery --name=Sender + type Sender interface { // Sends a read cmd to request some data Request(cmdClassifier model.CmdClassifierType, senderAddress, destinationAddress *model.FeatureAddressType, ackRequest bool, cmd []model.CmdType) (*model.MsgCounterType, error) @@ -33,22 +35,38 @@ type Sender interface { Notify(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) // Sends a write cmd, setting properties of remote features Write(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) + // return the datagram for a given msgCounter (only availbe for Notify messasges!), error if not found + DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) } type SenderImpl struct { msgNum uint64 // 64bit values need to be defined on top of the struct to make atomic commands work on 32bit systems + // we cache the last 100 notify messages, so we can find the matching item for result errors being returned + datagramNotifyCache *lru.Cache[model.MsgCounterType, model.DatagramType] + writeHandler SpineDataConnection } var _ Sender = (*SenderImpl)(nil) func NewSender(writeI SpineDataConnection) Sender { + cache, _ := lru.New[model.MsgCounterType, model.DatagramType](100) return &SenderImpl{ - writeHandler: writeI, + datagramNotifyCache: cache, + writeHandler: writeI, } } +// return the datagram for a given msgCounter (only availbe for Notify messasges!), error if not found +func (c *SenderImpl) DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) { + if datagram, ok := c.datagramNotifyCache.Get(msgCounter); ok { + return datagram, nil + } + + return model.DatagramType{}, errors.New("msgCounter not found") +} + func (c *SenderImpl) sendSpineMessage(datagram model.DatagramType) error { // pack into datagram data := model.Datagram{ @@ -192,6 +210,8 @@ func (c *SenderImpl) Notify(senderAddress, destinationAddress *model.FeatureAddr }, } + c.datagramNotifyCache.Add(*msgCounter, datagram) + return msgCounter, c.sendSpineMessage(datagram) } diff --git a/spine/sender_test.go b/spine/send_test.go similarity index 63% rename from spine/sender_test.go rename to spine/send_test.go index 776454e5..025eb4e8 100644 --- a/spine/sender_test.go +++ b/spine/send_test.go @@ -1,10 +1,11 @@ -package spine +package spine_test import ( "encoding/json" "sync" "testing" + "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" @@ -16,7 +17,7 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ SpineDataConnection = (*WriteMessageHandler)(nil) +var _ spine.SpineDataConnection = (*WriteMessageHandler)(nil) func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { t.mux.Lock() @@ -27,10 +28,10 @@ func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { func TestSender_Notify_MsgCounter(t *testing.T) { temp := &WriteMessageHandler{} - sut := NewSender(temp) + sut := spine.NewSender(temp) - senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) + senderAddress := featureAddressType(1, spine.NewEntityAddressType("Sender", []uint{1})) + destinationAddress := featureAddressType(2, spine.NewEntityAddressType("destination", []uint{1})) cmd := model.CmdType{ ResultData: &model.ResultDataType{ErrorNumber: util.Ptr(model.ErrorNumberType(model.ErrorNumberTypeNoError))}, } @@ -48,3 +49,13 @@ func TestSender_Notify_MsgCounter(t *testing.T) { assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) } + +func featureAddressType(id uint, entityAddress *model.EntityAddressType) *model.FeatureAddressType { + res := model.FeatureAddressType{ + Device: entityAddress.Device, + Entity: entityAddress.Entity, + Feature: util.Ptr(model.AddressFeatureType(id)), + } + + return &res +} diff --git a/spine/subscription_manager.go b/spine/subscription_manager.go index 281051ad..b8254b1a 100644 --- a/spine/subscription_manager.go +++ b/spine/subscription_manager.go @@ -4,14 +4,16 @@ import ( "errors" "fmt" "reflect" + "sync" "sync/atomic" "github.com/ahmetb/go-linq/v3" "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" ) type SubscriptionManager interface { - AddSubscription(localDevice *DeviceLocalImpl, remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error + AddSubscription(remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error RemoveSubscriptionsForEntity(remoteEntity *EntityRemoteImpl) Subscriptions(remoteDevice *DeviceRemoteImpl) []*SubscriptionEntry @@ -25,25 +27,30 @@ type SubscriptionEntry struct { } type SubscriptionManagerImpl struct { + localDevice *DeviceLocalImpl + subscriptionNum uint64 subscriptionEntries []*SubscriptionEntry + + mux sync.Mutex // TODO: add persistence } -func NewSubscriptionManager() SubscriptionManager { +func NewSubscriptionManager(localDevice *DeviceLocalImpl) SubscriptionManager { c := &SubscriptionManagerImpl{ subscriptionNum: 0, + localDevice: localDevice, } return c } // is sent from the client (remote device) to the server (local device) -func (c *SubscriptionManagerImpl) AddSubscription(localDevice *DeviceLocalImpl, remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error { +func (c *SubscriptionManagerImpl) AddSubscription(remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error { - serverFeature := localDevice.FeatureByAddress(data.ServerAddress) + serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) if serverFeature == nil { - return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *localDevice.Address()) + return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *c.localDevice.Address()) } if err := c.checkRoleAndType(serverFeature, model.RoleTypeServer, *data.ServerFeatureType); err != nil { return err @@ -63,6 +70,9 @@ func (c *SubscriptionManagerImpl) AddSubscription(localDevice *DeviceLocalImpl, clientFeature: clientFeature, } + c.mux.Lock() + defer c.mux.Unlock() + for _, item := range c.subscriptionEntries { if reflect.DeepEqual(item.serverFeature, serverFeature) && reflect.DeepEqual(item.clientFeature, clientFeature) { return fmt.Errorf("requested subscription is already present") @@ -85,8 +95,6 @@ func (c *SubscriptionManagerImpl) AddSubscription(localDevice *DeviceLocalImpl, // Remove a specific subscription that is provided by a delete message from a remote device func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error { - // TODO: test this!!! - var newSubscriptionEntries []*SubscriptionEntry // according to the spec 7.4.4 @@ -95,7 +103,8 @@ func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionMana // b. The absence of "subscriptionDelete. serverAddress. device" SHALL be treated as if it was // present and set to the recipient's "device" address part. - clientAddress := data.ClientAddress + var clientAddress model.FeatureAddressType + util.DeepCopy(data.ClientAddress, &clientAddress) if data.ClientAddress.Device == nil { clientAddress.Device = remoteDevice.Address() } @@ -105,8 +114,19 @@ func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionMana return fmt.Errorf("client feature '%s' in remote device '%s' not found", data.ClientAddress, *remoteDevice.Address()) } + serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) + if serverFeature == nil { + return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *c.localDevice.Address()) + } + + c.mux.Lock() + defer c.mux.Unlock() + for _, item := range c.subscriptionEntries { - if !reflect.DeepEqual(item.clientFeature.Address(), clientAddress) { + itemAddress := item.clientFeature.Address() + + if !reflect.DeepEqual(*itemAddress, clientAddress) && + !reflect.DeepEqual(item.serverFeature, serverFeature) { newSubscriptionEntries = append(newSubscriptionEntries, item) } } @@ -136,6 +156,9 @@ func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity *Ent return } + c.mux.Lock() + defer c.mux.Unlock() + var newSubscriptionEntries []*SubscriptionEntry for _, item := range c.subscriptionEntries { if !reflect.DeepEqual(item.clientFeature.Address().Entity, remoteEntity.Address().Entity) { @@ -160,6 +183,9 @@ func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity *Ent func (c *SubscriptionManagerImpl) Subscriptions(remoteDevice *DeviceRemoteImpl) []*SubscriptionEntry { var result []*SubscriptionEntry + c.mux.Lock() + defer c.mux.Unlock() + linq.From(c.subscriptionEntries).WhereT(func(s *SubscriptionEntry) bool { return s.clientFeature.Device().Ski() == remoteDevice.Ski() }).ToSlice(&result) @@ -170,6 +196,9 @@ func (c *SubscriptionManagerImpl) Subscriptions(remoteDevice *DeviceRemoteImpl) func (c *SubscriptionManagerImpl) SubscriptionsOnFeature(featureAddress model.FeatureAddressType) []*SubscriptionEntry { var result []*SubscriptionEntry + c.mux.Lock() + defer c.mux.Unlock() + linq.From(c.subscriptionEntries).WhereT(func(s *SubscriptionEntry) bool { return reflect.DeepEqual(*s.serverFeature.Address(), featureAddress) }).ToSlice(&result) diff --git a/spine/subscription_manager_test.go b/spine/subscription_manager_test.go new file mode 100644 index 00000000..562d506b --- /dev/null +++ b/spine/subscription_manager_test.go @@ -0,0 +1,82 @@ +package spine_test + +import ( + "testing" + "time" + + "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestSubscriptionManagerSuite(t *testing.T) { + suite.Run(t, new(SubscriptionManagerSuite)) +} + +type SubscriptionManagerSuite struct { + suite.Suite + + localDevice *spine.DeviceLocalImpl + remoteDevice *spine.DeviceRemoteImpl + sut spine.SubscriptionManager +} + +func (suite *SubscriptionManagerSuite) WriteSpineMessage([]byte) {} + +func (suite *SubscriptionManagerSuite) SetupSuite() { + suite.localDevice = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + + ski := "test" + sender := spine.NewSender(suite) + suite.remoteDevice = spine.NewDeviceRemoteImpl(suite.localDevice, ski, sender) + + suite.localDevice.AddRemoteDevice(ski, suite) + + suite.sut = spine.NewSubscriptionManager(suite.localDevice) +} + +func (suite *SubscriptionManagerSuite) Test_Subscriptions() { + entity := spine.NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) + suite.localDevice.AddEntity(entity) + + localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + + remoteEntity := spine.NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + suite.remoteDevice.AddEntity(remoteEntity) + + remoteFeature := spine.NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + remoteFeature.Address().Device = util.Ptr(model.AddressDeviceType("remoteDevice")) + remoteEntity.AddFeature(remoteFeature) + + subscrRequest := model.SubscriptionManagementRequestCallType{ + ClientAddress: remoteFeature.Address(), + ServerAddress: localFeature.Address(), + ServerFeatureType: util.Ptr(model.FeatureTypeTypeDeviceDiagnosis), + } + + subMgr := suite.localDevice.SubscriptionManager() + err := subMgr.AddSubscription(suite.remoteDevice, subscrRequest) + assert.Nil(suite.T(), err) + + err = subMgr.AddSubscription(suite.remoteDevice, subscrRequest) + assert.NotNil(suite.T(), err) + + subs := subMgr.Subscriptions(suite.remoteDevice) + assert.Equal(suite.T(), 1, len(subs)) + + subscrDelete := model.SubscriptionManagementDeleteCallType{ + ClientAddress: remoteFeature.Address(), + ServerAddress: localFeature.Address(), + } + + err = subMgr.RemoveSubscription(subscrDelete, suite.remoteDevice) + assert.Nil(suite.T(), err) + + subs = subMgr.Subscriptions(suite.remoteDevice) + assert.Equal(suite.T(), 0, len(subs)) + + err = subMgr.RemoveSubscription(subscrDelete, suite.remoteDevice) + assert.NotNil(suite.T(), err) +} diff --git a/util/helper.go b/util/helper.go index 4c5aef9c..f02ab959 100644 --- a/util/helper.go +++ b/util/helper.go @@ -1,6 +1,9 @@ package util -import "strings" +import ( + "encoding/json" + "strings" +) // standardize the provided SKI strings func NormalizeSKI(ski string) string { @@ -10,3 +13,9 @@ func NormalizeSKI(ski string) string { return ski } + +// quick way to a struct into another +func DeepCopy[A any](source, dest A) { + byt, _ := json.Marshal(source) + _ = json.Unmarshal(byt, dest) +} From ae3845d812e976466f33b036defa7626fd4d510a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 11:08:15 +0100 Subject: [PATCH 077/240] Automate assignment of function data --- spine/function_data_cmd.go | 749 +------------------------- spine/model/commandframe_additions.go | 85 +++ 2 files changed, 88 insertions(+), 746 deletions(-) diff --git a/spine/function_data_cmd.go b/spine/function_data_cmd.go index afc4c84d..aa2a7509 100644 --- a/spine/function_data_cmd.go +++ b/spine/function_data_cmd.go @@ -106,204 +106,7 @@ func filterEmptyPartial() []model.FilterType { func addSelectorToFilter[T any](filter model.FilterType, function model.FunctionType, data *T) model.FilterType { result := filter - switch function { - case model.FunctionTypeAlarmListData: - result.AlarmListDataSelectors = castData[model.AlarmListDataSelectorsType](data) - case model.FunctionTypeBillConstraintsListData: - result.BillConstraintsListDataSelectors = castData[model.BillConstraintsListDataSelectorsType](data) - case model.FunctionTypeBillDescriptionListData: - result.BillDescriptionListDataSelectors = castData[model.BillDescriptionListDataSelectorsType](data) - case model.FunctionTypeBillListData: - result.BillListDataSelectors = castData[model.BillListDataSelectorsType](data) - case model.FunctionTypeBindingManagementEntryListData: - result.BindingManagementEntryListDataSelectors = castData[model.BindingManagementEntryListDataSelectorsType](data) - case model.FunctionTypeCommodityListData: - result.CommodityListDataSelectors = castData[model.CommodityListDataSelectorsType](data) - case model.FunctionTypeDeviceConfigurationKeyValueConstraintsListData: - result.DeviceConfigurationKeyValueConstraintsListDataSelectors = castData[model.DeviceConfigurationKeyValueConstraintsListDataSelectorsType](data) - case model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData: - result.DeviceConfigurationKeyValueDescriptionListDataSelectors = castData[model.DeviceConfigurationKeyValueDescriptionListDataSelectorsType](data) - case model.FunctionTypeDeviceConfigurationKeyValueListData: - result.DeviceConfigurationKeyValueListDataSelectors = castData[model.DeviceConfigurationKeyValueListDataSelectorsType](data) - case model.FunctionTypeDirectControlActivityListData: - result.DirectControlActivityListDataSelectors = castData[model.DirectControlActivityListDataSelectorsType](data) - case model.FunctionTypeElectricalConnectionDescriptionListData: - filter.ElectricalConnectionDescriptionListDataSelectors = castData[model.ElectricalConnectionDescriptionListDataSelectorsType](data) - case model.FunctionTypeElectricalConnectionParameterDescriptionListData: - filter.ElectricalConnectionParameterDescriptionListDataSelectors = castData[model.ElectricalConnectionParameterDescriptionListDataSelectorsType](data) - case model.FunctionTypeElectricalConnectionPermittedValueSetListData: - result.ElectricalConnectionPermittedValueSetListDataSelectors = castData[model.ElectricalConnectionPermittedValueSetListDataSelectorsType](data) - case model.FunctionTypeElectricalConnectionStateListData: - result.ElectricalConnectionStateListDataSelectors = castData[model.ElectricalConnectionStateListDataSelectorsType](data) - case model.FunctionTypeElectricalConnectionCharacteristicListData: - result.ElectricalConnectionCharacteristicListDataSelectors = castData[model.ElectricalConnectionCharacteristicListDataSelectorsType](data) - case model.FunctionTypeHvacOperationModeDescriptionListData: - result.HvacOperationModeDescriptionListDataSelectors = castData[model.HvacOperationModeDescriptionListDataSelectorsType](data) - case model.FunctionTypeHvacOverrunDescriptionListData: - result.HvacOverrunDescriptionListDataSelectors = castData[model.HvacOverrunDescriptionListDataSelectorsType](data) - case model.FunctionTypeHvacOverrunListData: - result.HvacOverrunListDataSelectors = castData[model.HvacOverrunListDataSelectorsType](data) - case model.FunctionTypeHvacSystemFunctionDescriptionListData: - result.HvacSystemFunctionDescriptionListDataSelectors = castData[model.HvacSystemFunctionDescriptionListDataSelectorsType](data) - case model.FunctionTypeHvacSystemFunctionListData: - result.HvacSystemFunctionListDataSelectors = castData[model.HvacSystemFunctionListDataSelectorsType](data) - case model.FunctionTypeHvacSystemFunctionOperationModeRelationListData: - result.HvacSystemFunctionOperationModeRelationListDataSelectors = castData[model.HvacSystemFunctionOperationModeRelationListDataSelectorsType](data) - case model.FunctionTypeHvacSystemFunctionPowerSequenceRelationListData: - result.HvacSystemFunctionPowerSequenceRelationListDataSelectors = castData[model.HvacSystemFunctionPowerSequenceRelationListDataSelectorsType](data) - case model.FunctionTypeHvacSystemFunctionSetPointRelationListData: - result.HvacSystemFunctionSetpointRelationListDataSelectors = castData[model.HvacSystemFunctionSetpointRelationListDataSelectorsType](data) - case model.FunctionTypeIdentificationListData: - result.IdentificationListDataSelectors = castData[model.IdentificationListDataSelectorsType](data) - case model.FunctionTypeIncentiveDescriptionListData: - result.IncentiveDescriptionListDataSelectors = castData[model.IncentiveDescriptionListDataSelectorsType](data) - case model.FunctionTypeIncentiveListData: - result.IncentiveListDataSelectors = castData[model.IncentiveListDataSelectorsType](data) - case model.FunctionTypeIncentiveTableConstraintsData: - result.IncentiveTableConstraintsDataSelectors = castData[model.IncentiveTableConstraintsDataSelectorsType](data) - case model.FunctionTypeIncentiveTableData: - result.IncentiveTableDataSelectors = castData[model.IncentiveTableDataSelectorsType](data) - case model.FunctionTypeIncentiveTableDescriptionData: - result.IncentiveTableDescriptionDataSelectors = castData[model.IncentiveTableDescriptionDataSelectorsType](data) - case model.FunctionTypeLoadControlEventListData: - result.LoadControlEventListDataSelectors = castData[model.LoadControlEventListDataSelectorsType](data) - case model.FunctionTypeLoadControlLimitConstraintsListData: - result.LoadControlLimitConstraintsListDataSelectors = castData[model.LoadControlLimitConstraintsListDataSelectorsType](data) - case model.FunctionTypeLoadControlLimitDescriptionListData: - result.LoadControlLimitDescriptionListDataSelectors = castData[model.LoadControlLimitDescriptionListDataSelectorsType](data) - case model.FunctionTypeLoadControlLimitListData: - result.LoadControlLimitListDataSelectors = castData[model.LoadControlLimitListDataSelectorsType](data) - case model.FunctionTypeLoadControlStateListData: - result.LoadControlStateListDataSelectors = castData[model.LoadControlStateListDataSelectorsType](data) - case model.FunctionTypeMeasurementConstraintsListData: - result.MeasurementConstraintsListDataSelectors = castData[model.MeasurementConstraintsListDataSelectorsType](data) - case model.FunctionTypeMeasurementDescriptionListData: - result.MeasurementDescriptionListDataSelectors = castData[model.MeasurementDescriptionListDataSelectorsType](data) - case model.FunctionTypeMeasurementListData: - result.MeasurementListDataSelectors = castData[model.MeasurementListDataSelectorsType](data) - case model.FunctionTypeMeasurementSeriesListData: - result.MeasurementSeriesListDataSelectors = castData[model.MeasurementSeriesListDataSelectorsType](data) - case model.FunctionTypeMeasurementThresholdRelationListData: - result.MeasurementThresholdRelationListDataSelectors = castData[model.MeasurementThresholdRelationListDataSelectorsType](data) - case model.FunctionTypeMessagingListData: - result.MessagingListDataSelectors = castData[model.MessagingListDataSelectorsType](data) - case model.FunctionTypeNetworkManagementDeviceDescriptionListData: - result.NetworkManagementDeviceDescriptionListDataSelectors = castData[model.NetworkManagementDeviceDescriptionListDataSelectorsType](data) - case model.FunctionTypeNetworkManagementEntityDescriptionListData: - result.NetworkManagementEntityDescriptionListDataSelectors = castData[model.NetworkManagementEntityDescriptionListDataSelectorsType](data) - case model.FunctionTypeNetworkManagementFeatureDescriptionListData: - result.NetworkManagementFeatureDescriptionListDataSelectors = castData[model.NetworkManagementFeatureDescriptionListDataSelectorsType](data) - case model.FunctionTypeNodeManagementBindingData: - result.NodeManagementBindingDataSelectors = castData[model.NodeManagementBindingDataSelectorsType](data) - case model.FunctionTypeNodeManagementDestinationListData: - result.NodeManagementDestinationListDataSelectors = castData[model.NodeManagementDestinationListDataSelectorsType](data) - case model.FunctionTypeNodeManagementDetailedDiscoveryData: - result.NodeManagementDetailedDiscoveryDataSelectors = castData[model.NodeManagementDetailedDiscoveryDataSelectorsType](data) - case model.FunctionTypeNodeManagementSubscriptionData: - result.NodeManagementSubscriptionDataSelectors = castData[model.NodeManagementSubscriptionDataSelectorsType](data) - case model.FunctionTypeNodeManagementUseCaseData: - result.NodeManagementUseCaseDataSelectors = castData[model.NodeManagementUseCaseDataSelectorsType](data) - case model.FunctionTypeOperatingConstraintsDurationListData: - result.OperatingConstraintsDurationListDataSelectors = castData[model.OperatingConstraintsDurationListDataSelectorsType](data) - case model.FunctionTypeOperatingConstraintsInterruptListData: - result.OperatingConstraintsInterruptListDataSelectors = castData[model.OperatingConstraintsInterruptListDataSelectorsType](data) - case model.FunctionTypeOperatingConstraintsPowerDescriptionListData: - result.OperatingConstraintsPowerDescriptionListDataSelectors = castData[model.OperatingConstraintsPowerDescriptionListDataSelectorsType](data) - case model.FunctionTypeOperatingConstraintsPowerLevelListData: - result.OperatingConstraintsPowerLevelListDataSelectors = castData[model.OperatingConstraintsPowerLevelListDataSelectorsType](data) - case model.FunctionTypeOperatingConstraintsPowerRangeListData: - result.OperatingConstraintsPowerRangeListDataSelectors = castData[model.OperatingConstraintsPowerRangeListDataSelectorsType](data) - case model.FunctionTypeOperatingConstraintsResumeImplicationListData: - result.OperatingConstraintsResumeImplicationListDataSelectors = castData[model.OperatingConstraintsResumeImplicationListDataSelectorsType](data) - case model.FunctionTypePowerSequenceAlternativesRelationListData: - result.PowerSequenceAlternativesRelationListDataSelectors = castData[model.PowerSequenceAlternativesRelationListDataSelectorsType](data) - case model.FunctionTypePowerSequenceDescriptionListData: - result.PowerSequenceDescriptionListDataSelectors = castData[model.PowerSequenceDescriptionListDataSelectorsType](data) - case model.FunctionTypePowerSequencePriceListData: - result.PowerSequencePriceListDataSelectors = castData[model.PowerSequencePriceListDataSelectorsType](data) - case model.FunctionTypePowerSequenceScheduleConstraintsListData: - result.PowerSequenceScheduleConstraintsListDataSelectors = castData[model.PowerSequenceScheduleConstraintsListDataSelectorsType](data) - case model.FunctionTypePowerSequenceScheduleListData: - result.PowerSequenceScheduleListDataSelectors = castData[model.PowerSequenceScheduleListDataSelectorsType](data) - case model.FunctionTypePowerSequenceSchedulePreferenceListData: - result.PowerSequenceSchedulePreferenceListDataSelectors = castData[model.PowerSequenceSchedulePreferenceListDataSelectorsType](data) - case model.FunctionTypePowerSequenceStateListData: - result.PowerSequenceStateListDataSelectors = castData[model.PowerSequenceStateListDataSelectorsType](data) - case model.FunctionTypePowerTimeSlotScheduleConstraintsListData: - result.PowerTimeSlotScheduleConstraintsListDataSelectors = castData[model.PowerTimeSlotScheduleConstraintsListDataSelectorsType](data) - case model.FunctionTypePowerTimeSlotScheduleListData: - result.PowerTimeSlotScheduleListDataSelectors = castData[model.PowerTimeSlotScheduleListDataSelectorsType](data) - case model.FunctionTypePowerTimeSlotValueListData: - result.PowerTimeSlotValueListDataSelectors = castData[model.PowerTimeSlotValueListDataSelectorsType](data) - case model.FunctionTypeSensingListData: - result.SensingListDataSelectors = castData[model.SensingListDataSelectorsType](data) - case model.FunctionTypeSessionIdentificationListData: - result.SessionIdentificationListDataSelectors = castData[model.SessionIdentificationListDataSelectorsType](data) - case model.FunctionTypeSessionMeasurementRelationListData: - result.SessionMeasurementRelationListDataSelectors = castData[model.SessionMeasurementRelationListDataSelectorsType](data) - case model.FunctionTypeSetpointConstraintsListData: - result.SetpointConstraintsListDataSelectors = castData[model.SetpointConstraintsListDataSelectorsType](data) - case model.FunctionTypeSetpointDescriptionListData: - result.SetpointDescriptionListDataSelectors = castData[model.SetpointDescriptionListDataSelectorsType](data) - case model.FunctionTypeSetpointListData: - result.SetpointListDataSelectors = castData[model.SetpointListDataSelectorsType](data) - case model.FunctionTypeSmartEnergyManagementPsData: - result.SmartEnergyManagementPsDataSelectors = castData[model.SmartEnergyManagementPsDataSelectorsType](data) - case model.FunctionTypeSmartEnergyManagementPsPriceData: - result.SmartEnergyManagementPsPriceDataSelectors = castData[model.SmartEnergyManagementPsPriceDataSelectorsType](data) - case model.FunctionTypeSpecificationVersionListData: - result.SpecificationVersionListDataSelectors = castData[model.SpecificationVersionListDataSelectorsType](data) - case model.FunctionTypeStateInformationListData: - result.StateInformationListDataSelectors = castData[model.StateInformationListDataSelectorsType](data) - case model.FunctionTypeSupplyConditionListData: - result.SupplyConditionListDataSelectors = castData[model.SupplyConditionListDataSelectorsType](data) - case model.FunctionTypeSupplyConditionThresholdRelationListData: - result.SupplyConditionThresholdRelationListDataSelectors = castData[model.SupplyConditionThresholdRelationListDataSelectorsType](data) - case model.FunctionTypeTariffBoundaryRelationListData: - result.TariffBoundaryRelationListDataSelectors = castData[model.TariffBoundaryRelationListDataSelectorsType](data) - case model.FunctionTypeTariffDescriptionListData: - result.TariffDescriptionListDataSelectors = castData[model.TariffDescriptionListDataSelectorsType](data) - case model.FunctionTypeTariffListData: - result.TariffListDataSelectors = castData[model.TariffListDataSelectorsType](data) - case model.FunctionTypeTariffTierRelationListData: - result.TariffTierRelationListDataSelectors = castData[model.TariffTierRelationListDataSelectorsType](data) - case model.FunctionTypeTaskManagementJobDescriptionListData: - result.TaskManagementJobDescriptionListDataSelectors = castData[model.TaskManagementJobDescriptionListDataSelectorsType](data) - case model.FunctionTypeTaskManagementJobListData: - result.TaskManagementJobListDataSelectors = castData[model.TaskManagementJobListDataSelectorsType](data) - case model.FunctionTypeTaskManagementJobRelationListData: - result.TaskManagementJobRelationListDataSelectors = castData[model.TaskManagementJobRelationListDataSelectorsType](data) - case model.FunctionTypeThresholdConstraintsListData: - result.ThresholdConstraintsListDataSelectors = castData[model.ThresholdConstraintsListDataSelectorsType](data) - case model.FunctionTypeThresholdDescriptionListData: - result.ThresholdDescriptionListDataSelectors = castData[model.ThresholdDescriptionListDataSelectorsType](data) - case model.FunctionTypeThresholdListData: - result.ThresholdListDataSelectors = castData[model.ThresholdListDataSelectorsType](data) - case model.FunctionTypeTierBoundaryDescriptionListData: - result.TierBoundaryDescriptionListDataSelectors = castData[model.TierBoundaryDescriptionListDataSelectorsType](data) - case model.FunctionTypeTierBoundaryListData: - result.TierBoundaryListDataSelectors = castData[model.TierBoundaryListDataSelectorsType](data) - case model.FunctionTypeTierDescriptionListData: - result.TierDescriptionListDataSelectors = castData[model.TierDescriptionListDataSelectorsType](data) - case model.FunctionTypeTierIncentiveRelationListData: - result.TierIncentiveRelationListDataSelectors = castData[model.TierIncentiveRelationListDataSelectorsType](data) - case model.FunctionTypeTierListData: - result.TierListDataSelectors = castData[model.TierListDataSelectorsType](data) - case model.FunctionTypeTimeSeriesConstraintsListData: - result.TimeSeriesConstraintsListDataSelectors = castData[model.TimeSeriesConstraintsListDataSelectorsType](data) - case model.FunctionTypeTimeSeriesDescriptionListData: - result.TimeSeriesDescriptionListDataSelectors = castData[model.TimeSeriesDescriptionListDataSelectorsType](data) - case model.FunctionTypeTimeSeriesListData: - result.TimeSeriesListDataSelectors = castData[model.TimeSeriesListDataSelectorsType](data) - case model.FunctionTypeTimeTableConstraintsListData: - result.TimeTableConstraintsListDataSelectors = castData[model.TimeTableConstraintsListDataSelectorsType](data) - case model.FunctionTypeTimeTableDescriptionListData: - result.TimeTableDescriptionListDataSelectors = castData[model.TimeTableDescriptionListDataSelectorsType](data) - case model.FunctionTypeTimeTableListData: - result.TimeTableListDataSelectors = castData[model.TimeTableListDataSelectorsType](data) - case model.FunctionTypeUseCaseInformationListData: - result.UseCaseInformationListDataSelectors = castData[model.UseCaseInformationListDataSelectorsType](data) - } + result.SetDataForFunction(model.EEBusTagTypeTypeSelector, function, data) return result } @@ -311,288 +114,7 @@ func addSelectorToFilter[T any](filter model.FilterType, function model.Function func addElementToFilter[T any](filter model.FilterType, function model.FunctionType, data *T) model.FilterType { result := filter - switch function { - case model.FunctionTypeActuatorLevelData: - result.ActuatorLevelDataElements = castData[model.ActuatorLevelDataElementsType](data) - case model.FunctionTypeActuatorLevelDescriptionData: - result.ActuatorLevelDescriptionDataElements = castData[model.ActuatorLevelDescriptionDataElementsType](data) - case model.FunctionTypeActuatorSwitchData: - result.ActuatorSwitchDataElements = castData[model.ActuatorSwitchDataElementsType](data) - case model.FunctionTypeActuatorSwitchDescriptionData: - result.ActuatorSwitchDescriptionDataElements = castData[model.ActuatorSwitchDescriptionDataElementsType](data) - case model.FunctionTypeAlarmListData: - result.AlarmDataElements = castData[model.AlarmDataElementsType](data) - case model.FunctionTypeBillConstraintsListData: - result.BillConstraintsDataElements = castData[model.BillConstraintsDataElementsType](data) - case model.FunctionTypeBillDescriptionListData: - result.BillDescriptionDataElements = castData[model.BillDescriptionDataElementsType](data) - case model.FunctionTypeBillListData: - result.BillDataElements = castData[model.BillDataElementsType](data) - case model.FunctionTypeBindingManagementDeleteCall: - result.BindingManagementDeleteCallElements = castData[model.BindingManagementDeleteCallElementsType](data) - case model.FunctionTypeBindingManagementEntryListData: - result.BindingManagementEntryDataElements = castData[model.BindingManagementEntryDataElementsType](data) - case model.FunctionTypeBindingManagementRequestCall: - result.BindingManagementRequestCallElements = castData[model.BindingManagementRequestCallElementsType](data) - case model.FunctionTypeCommodityListData: - result.CommodityDataElements = castData[model.CommodityDataElementsType](data) - case model.FunctionTypeDataTunnelingCall: - result.DataTunnelingCallElements = castData[model.DataTunnelingCallElementsType](data) - case model.FunctionTypeDeviceClassificationManufacturerData: - result.DeviceClassificationManufacturerDataElements = castData[model.DeviceClassificationManufacturerDataElementsType](data) - case model.FunctionTypeDeviceClassificationUserData: - result.DeviceClassificationUserDataElements = castData[model.DeviceClassificationUserDataElementsType](data) - case model.FunctionTypeDeviceConfigurationKeyValueConstraintsListData: - result.DeviceConfigurationKeyValueConstraintsDataElements = castData[model.DeviceConfigurationKeyValueConstraintsDataElementsType](data) - case model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData: - result.DeviceConfigurationKeyValueDescriptionDataElements = castData[model.DeviceConfigurationKeyValueDescriptionDataElementsType](data) - case model.FunctionTypeDeviceConfigurationKeyValueListData: - result.DeviceConfigurationKeyValueDataElements = castData[model.DeviceConfigurationKeyValueDataElementsType](data) - case model.FunctionTypeDeviceDiagnosisHeartbeatData: - result.DeviceDiagnosisHeartbeatDataElements = castData[model.DeviceDiagnosisHeartbeatDataElementsType](data) - case model.FunctionTypeDeviceDiagnosisServiceData: - result.DeviceDiagnosisServiceDataElements = castData[model.DeviceDiagnosisServiceDataElementsType](data) - case model.FunctionTypeDeviceDiagnosisStateData: - result.DeviceDiagnosisStateDataElements = castData[model.DeviceDiagnosisStateDataElementsType](data) - case model.FunctionTypeDirectControlActivityListData: - result.DirectControlActivityDataElements = castData[model.DirectControlActivityDataElementsType](data) - case model.FunctionTypeDirectControlDescriptionData: - result.DirectControlDescriptionDataElements = castData[model.DirectControlDescriptionDataElementsType](data) - case model.FunctionTypeElectricalConnectionDescriptionListData: - result.ElectricalConnectionDescriptionDataElements = castData[model.ElectricalConnectionDescriptionDataElementsType](data) - case model.FunctionTypeElectricalConnectionParameterDescriptionListData: - result.ElectricalConnectionParameterDescriptionDataElements = castData[model.ElectricalConnectionParameterDescriptionDataElementsType](data) - case model.FunctionTypeElectricalConnectionPermittedValueSetListData: - result.ElectricalConnectionPermittedValueSetDataElements = castData[model.ElectricalConnectionPermittedValueSetDataElementsType](data) - case model.FunctionTypeElectricalConnectionStateListData: - result.ElectricalConnectionStateDataElements = castData[model.ElectricalConnectionStateDataElementsType](data) - case model.FunctionTypeElectricalConnectionCharacteristicListData: - result.ElectricalConnectionCharacteristicDataElements = castData[model.ElectricalConnectionCharacteristicDataElementsType](data) - case model.FunctionTypeHvacOperationModeDescriptionListData: - result.HvacOperationModeDescriptionDataElements = castData[model.HvacOperationModeDescriptionDataElementsType](data) - case model.FunctionTypeHvacOverrunDescriptionListData: - result.HvacOverrunDescriptionDataElements = castData[model.HvacOverrunDescriptionDataElementsType](data) - case model.FunctionTypeHvacOverrunListData: - result.HvacOverrunDataElements = castData[model.HvacOverrunDataElementsType](data) - case model.FunctionTypeHvacSystemFunctionDescriptionListData: - result.HvacSystemFunctionDescriptionDataElements = castData[model.HvacSystemFunctionDescriptionDataElementsType](data) - case model.FunctionTypeHvacSystemFunctionListData: - result.HvacSystemFunctionDataElements = castData[model.HvacSystemFunctionDataElementsType](data) - case model.FunctionTypeHvacSystemFunctionOperationModeRelationListData: - result.HvacSystemFunctionOperationModeRelationDataElements = castData[model.HvacSystemFunctionOperationModeRelationDataElementsType](data) - case model.FunctionTypeHvacSystemFunctionPowerSequenceRelationListData: - result.HvacSystemFunctionPowerSequenceRelationDataElements = castData[model.HvacSystemFunctionPowerSequenceRelationDataElementsType](data) - case model.FunctionTypeHvacSystemFunctionSetPointRelationListData: - result.HvacSystemFunctionSetpointRelationDataElements = castData[model.HvacSystemFunctionSetpointRelationDataElementsType](data) - case model.FunctionTypeIdentificationListData: - result.IdentificationDataElements = castData[model.IdentificationDataElementsType](data) - case model.FunctionTypeIncentiveDescriptionListData: - result.IncentiveDescriptionDataElements = castData[model.IncentiveDescriptionDataElementsType](data) - case model.FunctionTypeIncentiveListData: - result.IncentiveDataElements = castData[model.IncentiveDataElementsType](data) - case model.FunctionTypeIncentiveTableConstraintsData: - result.IncentiveTableConstraintsDataElements = castData[model.IncentiveTableConstraintsDataElementsType](data) - case model.FunctionTypeIncentiveTableData: - result.IncentiveTableDataElements = castData[model.IncentiveTableDataElementsType](data) - case model.FunctionTypeIncentiveTableDescriptionData: - result.IncentiveTableDescriptionDataElements = castData[model.IncentiveTableDescriptionDataElementsType](data) - case model.FunctionTypeLoadControlEventListData: - result.LoadControlEventDataElements = castData[model.LoadControlEventDataElementsType](data) - case model.FunctionTypeLoadControlLimitConstraintsListData: - result.LoadControlLimitConstraintsDataElements = castData[model.LoadControlLimitConstraintsDataElementsType](data) - case model.FunctionTypeLoadControlLimitDescriptionListData: - result.LoadControlLimitDescriptionDataElements = castData[model.LoadControlLimitDescriptionDataElementsType](data) - case model.FunctionTypeLoadControlLimitListData: - result.LoadControlLimitDataElements = castData[model.LoadControlLimitDataElementsType](data) - case model.FunctionTypeLoadControlNodeData: - result.LoadControlNodeDataElements = castData[model.LoadControlNodeDataElementsType](data) - case model.FunctionTypeLoadControlStateListData: - result.LoadControlStateDataElements = castData[model.LoadControlStateDataElementsType](data) - case model.FunctionTypeMeasurementConstraintsListData: - result.MeasurementConstraintsDataElements = castData[model.MeasurementConstraintsDataElementsType](data) - case model.FunctionTypeMeasurementDescriptionListData: - result.MeasurementDescriptionDataElements = castData[model.MeasurementDescriptionDataElementsType](data) - case model.FunctionTypeMeasurementListData: - result.MeasurementDataElements = castData[model.MeasurementDataElementsType](data) - case model.FunctionTypeMeasurementSeriesListData: - result.MeasurementSeriesDataElements = castData[model.MeasurementSeriesDataElementsType](data) - case model.FunctionTypeMeasurementThresholdRelationListData: - result.MeasurementThresholdRelationDataElements = castData[model.MeasurementThresholdRelationDataElementsType](data) - case model.FunctionTypeMessagingListData: - result.MessagingDataElements = castData[model.MessagingDataElementsType](data) - case model.FunctionTypeNetworkManagementAbortCall: - result.NetworkManagementAbortCallElements = castData[model.NetworkManagementAbortCallElementsType](data) - case model.FunctionTypeNetworkManagementAddNodeCall: - result.NetworkManagementAddNodeCallElements = castData[model.NetworkManagementAddNodeCallElementsType](data) - case model.FunctionTypeNetworkManagementDeviceDescriptionListData: - result.NetworkManagementDeviceDescriptionDataElements = castData[model.NetworkManagementDeviceDescriptionDataElementsType](data) - case model.FunctionTypeNetworkManagementDiscoverCall: - result.NetworkManagementDiscoverCallElements = castData[model.NetworkManagementDiscoverCallElementsType](data) - case model.FunctionTypeNetworkManagementEntityDescriptionListData: - result.NetworkManagementEntityDescriptionDataElements = castData[model.NetworkManagementEntityDescriptionDataElementsType](data) - case model.FunctionTypeNetworkManagementFeatureDescriptionListData: - result.NetworkManagementFeatureDescriptionDataElements = castData[model.NetworkManagementFeatureDescriptionDataElementsType](data) - case model.FunctionTypeNetworkManagementJoiningModeData: - result.NetworkManagementJoiningModeDataElements = castData[model.NetworkManagementJoiningModeDataElementsType](data) - case model.FunctionTypeNetworkManagementModifyNodeCall: - result.NetworkManagementModifyNodeCallElements = castData[model.NetworkManagementModifyNodeCallElementsType](data) - case model.FunctionTypeNetworkManagementProcessStateData: - result.NetworkManagementProcessStateDataElements = castData[model.NetworkManagementProcessStateDataElementsType](data) - case model.FunctionTypeNetworkManagementRemoveNodeCall: - result.NetworkManagementRemoveNodeCallElements = castData[model.NetworkManagementRemoveNodeCallElementsType](data) - case model.FunctionTypeNetworkManagementReportCandidateData: - result.NetworkManagementReportCandidateDataElements = castData[model.NetworkManagementReportCandidateDataElementsType](data) - case model.FunctionTypeNetworkManagementScanNetworkCall: - result.NetworkManagementScanNetworkCallElements = castData[model.NetworkManagementScanNetworkCallElementsType](data) - case model.FunctionTypeNodeManagementBindingData: - result.NodeManagementBindingDataElements = castData[model.NodeManagementBindingDataElementsType](data) - case model.FunctionTypeNodeManagementBindingDeleteCall: - result.NodeManagementBindingDeleteCallElements = castData[model.NodeManagementBindingDeleteCallElementsType](data) - case model.FunctionTypeNodeManagementBindingRequestCall: - result.NodeManagementBindingRequestCallElements = castData[model.NodeManagementBindingRequestCallElementsType](data) - case model.FunctionTypeNodeManagementDestinationListData: - result.NodeManagementDestinationDataElements = castData[model.NodeManagementDestinationDataElementsType](data) - case model.FunctionTypeNodeManagementDetailedDiscoveryData: - result.NodeManagementDetailedDiscoveryDataElements = castData[model.NodeManagementDetailedDiscoveryDataElementsType](data) - case model.FunctionTypeNodeManagementSubscriptionData: - result.NodeManagementSubscriptionDataElements = castData[model.NodeManagementSubscriptionDataElementsType](data) - case model.FunctionTypeNodeManagementSubscriptionDeleteCall: - result.NodeManagementSubscriptionDeleteCallElements = castData[model.NodeManagementSubscriptionDeleteCallElementsType](data) - case model.FunctionTypeNodeManagementSubscriptionRequestCall: - result.NodeManagementSubscriptionRequestCallElements = castData[model.NodeManagementSubscriptionRequestCallElementsType](data) - case model.FunctionTypeNodeManagementUseCaseData: - result.NodeManagementUseCaseDataElements = castData[model.NodeManagementUseCaseDataElementsType](data) - case model.FunctionTypeOperatingConstraintsDurationListData: - result.OperatingConstraintsDurationDataElements = castData[model.OperatingConstraintsDurationDataElementsType](data) - case model.FunctionTypeOperatingConstraintsInterruptListData: - result.OperatingConstraintsInterruptDataElements = castData[model.OperatingConstraintsInterruptDataElementsType](data) - case model.FunctionTypeOperatingConstraintsPowerDescriptionListData: - result.OperatingConstraintsPowerDescriptionDataElements = castData[model.OperatingConstraintsPowerDescriptionDataElementsType](data) - case model.FunctionTypeOperatingConstraintsPowerLevelListData: - result.OperatingConstraintsPowerLevelDataElements = castData[model.OperatingConstraintsPowerLevelDataElementsType](data) - case model.FunctionTypeOperatingConstraintsPowerRangeListData: - result.OperatingConstraintsPowerRangeDataElements = castData[model.OperatingConstraintsPowerRangeDataElementsType](data) - case model.FunctionTypeOperatingConstraintsResumeImplicationListData: - result.OperatingConstraintsResumeImplicationDataElements = castData[model.OperatingConstraintsResumeImplicationDataElementsType](data) - case model.FunctionTypePowerSequenceAlternativesRelationListData: - result.PowerSequenceAlternativesRelationDataElements = castData[model.PowerSequenceAlternativesRelationDataElementsType](data) - case model.FunctionTypePowerSequenceDescriptionListData: - result.PowerSequenceDescriptionDataElements = castData[model.PowerSequenceDescriptionDataElementsType](data) - case model.FunctionTypePowerSequenceNodeScheduleInformationData: - result.PowerSequenceNodeScheduleInformationDataElements = castData[model.PowerSequenceNodeScheduleInformationDataElementsType](data) - case model.FunctionTypePowerSequencePriceCalculationRequestCall: - result.PowerSequencePriceCalculationRequestCallElements = castData[model.PowerSequencePriceCalculationRequestCallElementsType](data) - case model.FunctionTypePowerSequencePriceListData: - result.PowerSequencePriceDataElements = castData[model.PowerSequencePriceDataElementsType](data) - case model.FunctionTypePowerSequenceScheduleConfigurationRequestCall: - result.PowerSequenceScheduleConfigurationRequestCallElements = castData[model.PowerSequenceScheduleConfigurationRequestCallElementsType](data) - case model.FunctionTypePowerSequenceScheduleConstraintsListData: - result.PowerSequenceScheduleConstraintsDataElements = castData[model.PowerSequenceScheduleConstraintsDataElementsType](data) - case model.FunctionTypePowerSequenceScheduleListData: - result.PowerSequenceScheduleDataElements = castData[model.PowerSequenceScheduleDataElementsType](data) - case model.FunctionTypePowerSequenceSchedulePreferenceListData: - result.PowerSequenceSchedulePreferenceDataElements = castData[model.PowerSequenceSchedulePreferenceDataElementsType](data) - case model.FunctionTypePowerSequenceStateListData: - result.PowerSequenceStateDataElements = castData[model.PowerSequenceStateDataElementsType](data) - case model.FunctionTypePowerTimeSlotScheduleConstraintsListData: - result.PowerTimeSlotScheduleConstraintsDataElements = castData[model.PowerTimeSlotScheduleConstraintsDataElementsType](data) - case model.FunctionTypePowerTimeSlotScheduleListData: - result.PowerTimeSlotScheduleDataElements = castData[model.PowerTimeSlotScheduleDataElementsType](data) - case model.FunctionTypePowerTimeSlotValueListData: - result.PowerTimeSlotValueDataElements = castData[model.PowerTimeSlotValueDataElementsType](data) - case model.FunctionTypeSensingListData: - result.SensingDataElements = castData[model.SensingDataElementsType](data) - case model.FunctionTypeSessionIdentificationListData: - result.SessionIdentificationDataElements = castData[model.SessionIdentificationDataElementsType](data) - case model.FunctionTypeSessionMeasurementRelationListData: - result.SessionMeasurementRelationDataElements = castData[model.SessionMeasurementRelationDataElementsType](data) - case model.FunctionTypeSetpointConstraintsListData: - result.SetpointConstraintsDataElements = castData[model.SetpointConstraintsDataElementsType](data) - case model.FunctionTypeSetpointDescriptionListData: - result.SensingDescriptionDataElements = castData[model.SensingDescriptionDataElementsType](data) - case model.FunctionTypeSetpointListData: - result.SetpointDataElements = castData[model.SetpointDataElementsType](data) - case model.FunctionTypeSmartEnergyManagementPsConfigurationRequestCall: - result.SmartEnergyManagementPsConfigurationRequestCallElements = castData[model.SmartEnergyManagementPsConfigurationRequestCallElementsType](data) - case model.FunctionTypeSmartEnergyManagementPsData: - result.SmartEnergyManagementPsDataElements = castData[model.SmartEnergyManagementPsDataElementsType](data) - case model.FunctionTypeSmartEnergyManagementPsPriceCalculationRequestCall: - result.SmartEnergyManagementPsPriceCalculationRequestCallElements = castData[model.SmartEnergyManagementPsPriceCalculationRequestCallElementsType](data) - case model.FunctionTypeSmartEnergyManagementPsPriceData: - result.SmartEnergyManagementPsPriceDataElements = castData[model.SmartEnergyManagementPsPriceDataElementsType](data) - case model.FunctionTypeSpecificationVersionListData: - result.SpecificationVersionDataElements = castData[model.SpecificationVersionDataElementsType](data) - case model.FunctionTypeStateInformationListData: - result.StateInformationDataElements = castData[model.StateInformationDataElementsType](data) - case model.FunctionTypeSubscriptionManagementDeleteCall: - result.SubscriptionManagementDeleteCallElements = castData[model.SubscriptionManagementDeleteCallElementsType](data) - case model.FunctionTypeSubscriptionManagementEntryListData: - result.SubscriptionManagementEntryDataElements = castData[model.SubscriptionManagementEntryDataElementsType](data) - case model.FunctionTypeSubscriptionManagementRequestCall: - result.SubscriptionManagementRequestCallElements = castData[model.SubscriptionManagementRequestCallElementsType](data) - case model.FunctionTypeSupplyConditionListData: - result.SupplyConditionDataElements = castData[model.SupplyConditionDataElementsType](data) - case model.FunctionTypeSupplyConditionDescriptionListData: - result.SupplyConditionDescriptionDataElements = castData[model.SupplyConditionDescriptionDataElementsType](data) - case model.FunctionTypeSupplyConditionThresholdRelationListData: - result.SupplyConditionThresholdRelationDataElements = castData[model.SupplyConditionThresholdRelationDataElementsType](data) - case model.FunctionTypeTariffBoundaryRelationListData: - result.TariffBoundaryRelationDataElements = castData[model.TariffBoundaryRelationDataElementsType](data) - case model.FunctionTypeTariffDescriptionListData: - result.TariffDescriptionDataElements = castData[model.TariffDescriptionDataElementsType](data) - case model.FunctionTypeTariffListData: - result.TariffDataElements = castData[model.TariffDataElementsType](data) - case model.FunctionTypeTariffOverallConstraintsData: - result.TariffOverallConstraintsDataElements = castData[model.TariffOverallConstraintsDataElementsType](data) - case model.FunctionTypeTariffTierRelationListData: - result.TariffTierRelationDataElements = castData[model.TariffTierRelationDataElementsType](data) - case model.FunctionTypeTaskManagementJobDescriptionListData: - result.TaskManagementJobDescriptionDataElements = castData[model.TaskManagementJobDescriptionDataElementsType](data) - case model.FunctionTypeTaskManagementJobListData: - result.TaskManagementJobDataElements = castData[model.TaskManagementJobDataElementsType](data) - case model.FunctionTypeTaskManagementJobRelationListData: - result.TaskManagementJobRelationDataElements = castData[model.TaskManagementJobRelationDataElementsType](data) - case model.FunctionTypeTaskManagementOverviewData: - result.TaskManagementOverviewDataElements = castData[model.TaskManagementOverviewDataElementsType](data) - case model.FunctionTypeThresholdConstraintsListData: - result.ThresholdConstraintsDataElements = castData[model.ThresholdConstraintsDataElementsType](data) - case model.FunctionTypeThresholdDescriptionListData: - result.ThresholdDescriptionDataElements = castData[model.ThresholdDescriptionDataElementsType](data) - case model.FunctionTypeThresholdListData: - result.ThresholdDataElements = castData[model.ThresholdDataElementsType](data) - case model.FunctionTypeTierBoundaryDescriptionListData: - result.TierBoundaryDescriptionDataElements = castData[model.TierBoundaryDescriptionDataElementsType](data) - case model.FunctionTypeTierBoundaryListData: - result.TierBoundaryDataElements = castData[model.TierBoundaryDataElementsType](data) - case model.FunctionTypeTierDescriptionListData: - result.TierDescriptionDataElements = castData[model.TierDescriptionDataElementsType](data) - case model.FunctionTypeTierIncentiveRelationListData: - result.TierIncentiveRelationDataElements = castData[model.TierIncentiveRelationDataElementsType](data) - case model.FunctionTypeTierListData: - result.TierDataElements = castData[model.TierDataElementsType](data) - case model.FunctionTypeTimeDistributorData: - result.TimeDistributorDataElements = castData[model.TimeDistributorDataElementsType](data) - case model.FunctionTypeTimeDistributorEnquiryCall: - result.TimeDistributorEnquiryCallElements = castData[model.TimeDistributorEnquiryCallElementsType](data) - case model.FunctionTypeTimeInformationData: - result.TimeInformationDataElements = castData[model.TimeInformationDataElementsType](data) - case model.FunctionTypeTimePrecisionData: - result.TimePrecisionDataElements = castData[model.TimePrecisionDataElementsType](data) - case model.FunctionTypeTimeSeriesConstraintsListData: - result.TimeSeriesConstraintsDataElements = castData[model.TimeSeriesConstraintsDataElementsType](data) - case model.FunctionTypeTimeSeriesDescriptionListData: - result.TimeSeriesDescriptionDataElements = castData[model.TimeSeriesDescriptionDataElementsType](data) - case model.FunctionTypeTimeSeriesListData: - result.TimeSeriesDataElements = castData[model.TimeSeriesDataElementsType](data) - case model.FunctionTypeTimeTableConstraintsListData: - result.TimeTableConstraintsDataElements = castData[model.TimeTableConstraintsDataElementsType](data) - case model.FunctionTypeTimeTableDescriptionListData: - result.TimeTableDescriptionDataElements = castData[model.TimeTableDescriptionDataElementsType](data) - case model.FunctionTypeTimeTableListData: - result.TimeTableDataElements = castData[model.TimeTableDataElementsType](data) - case model.FunctionTypeUseCaseInformationListData: - result.UseCaseInformationDataElements = castData[model.UseCaseInformationDataElementsType](data) - } + result.SetDataForFunction(model.EEbusTagTypeTypeElements, function, data) return result } @@ -600,272 +122,7 @@ func addElementToFilter[T any](filter model.FilterType, function model.FunctionT func createCmd[T any](function model.FunctionType, data *T) model.CmdType { result := model.CmdType{} - switch function { - case model.FunctionTypeActuatorLevelData: - result.ActuatorLevelData = castData[model.ActuatorLevelDataType](data) - case model.FunctionTypeActuatorLevelDescriptionData: - result.ActuatorLevelDescriptionData = castData[model.ActuatorLevelDescriptionDataType](data) - case model.FunctionTypeActuatorSwitchData: - result.ActuatorSwitchData = castData[model.ActuatorSwitchDataType](data) - case model.FunctionTypeActuatorSwitchDescriptionData: - result.ActuatorSwitchDescriptionData = castData[model.ActuatorSwitchDescriptionDataType](data) - case model.FunctionTypeAlarmListData: - result.AlarmListData = castData[model.AlarmListDataType](data) - case model.FunctionTypeBillConstraintsListData: - result.BillConstraintsListData = castData[model.BillConstraintsListDataType](data) - case model.FunctionTypeBillDescriptionListData: - result.BillDescriptionListData = castData[model.BillDescriptionListDataType](data) - case model.FunctionTypeBillListData: - result.BillListData = castData[model.BillListDataType](data) - case model.FunctionTypeBindingManagementDeleteCall: - result.BindingManagementDeleteCall = castData[model.BindingManagementDeleteCallType](data) - case model.FunctionTypeBindingManagementEntryListData: - result.BindingManagementEntryListData = castData[model.BindingManagementEntryListDataType](data) - case model.FunctionTypeBindingManagementRequestCall: - result.BindingManagementRequestCall = castData[model.BindingManagementRequestCallType](data) - case model.FunctionTypeCommodityListData: - result.CommodityListData = castData[model.CommodityListDataType](data) - case model.FunctionTypeDataTunnelingCall: - result.DataTunnelingCall = castData[model.DataTunnelingCallType](data) - case model.FunctionTypeDeviceClassificationManufacturerData: - result.DeviceClassificationManufacturerData = castData[model.DeviceClassificationManufacturerDataType](data) - case model.FunctionTypeDeviceClassificationUserData: - result.DeviceClassificationUserData = castData[model.DeviceClassificationUserDataType](data) - case model.FunctionTypeDeviceConfigurationKeyValueConstraintsListData: - result.DeviceConfigurationKeyValueConstraintsListData = castData[model.DeviceConfigurationKeyValueConstraintsListDataType](data) - case model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData: - result.DeviceConfigurationKeyValueDescriptionListData = castData[model.DeviceConfigurationKeyValueDescriptionListDataType](data) - case model.FunctionTypeDeviceConfigurationKeyValueListData: - result.DeviceConfigurationKeyValueListData = castData[model.DeviceConfigurationKeyValueListDataType](data) - case model.FunctionTypeDeviceDiagnosisHeartbeatData: - result.DeviceDiagnosisHeartbeatData = castData[model.DeviceDiagnosisHeartbeatDataType](data) - case model.FunctionTypeDeviceDiagnosisServiceData: - result.DeviceDiagnosisServiceData = castData[model.DeviceDiagnosisServiceDataType](data) - case model.FunctionTypeDeviceDiagnosisStateData: - result.DeviceDiagnosisStateData = castData[model.DeviceDiagnosisStateDataType](data) - case model.FunctionTypeDirectControlActivityListData: - result.DirectControlActivityListData = castData[model.DirectControlActivityListDataType](data) - case model.FunctionTypeDirectControlDescriptionData: - result.DirectControlDescriptionData = castData[model.DirectControlDescriptionDataType](data) - case model.FunctionTypeElectricalConnectionDescriptionListData: - result.ElectricalConnectionDescriptionListData = castData[model.ElectricalConnectionDescriptionListDataType](data) - case model.FunctionTypeElectricalConnectionParameterDescriptionListData: - result.ElectricalConnectionParameterDescriptionListData = castData[model.ElectricalConnectionParameterDescriptionListDataType](data) - case model.FunctionTypeElectricalConnectionPermittedValueSetListData: - result.ElectricalConnectionPermittedValueSetListData = castData[model.ElectricalConnectionPermittedValueSetListDataType](data) - case model.FunctionTypeElectricalConnectionStateListData: - result.ElectricalConnectionStateListData = castData[model.ElectricalConnectionStateListDataType](data) - case model.FunctionTypeElectricalConnectionCharacteristicListData: - result.ElectricalConnectionCharacteristicListData = castData[model.ElectricalConnectionCharacteristicListDataType](data) - case model.FunctionTypeHvacOperationModeDescriptionListData: - result.HvacOperationModeDescriptionListData = castData[model.HvacOperationModeDescriptionListDataType](data) - case model.FunctionTypeHvacOverrunDescriptionListData: - result.HvacOverrunDescriptionListData = castData[model.HvacOverrunDescriptionListDataType](data) - case model.FunctionTypeHvacOverrunListData: - result.HvacOverrunListData = castData[model.HvacOverrunListDataType](data) - case model.FunctionTypeHvacSystemFunctionDescriptionListData: - result.HvacSystemFunctionDescriptionListData = castData[model.HvacSystemFunctionDescriptionListDataType](data) - case model.FunctionTypeHvacSystemFunctionListData: - result.HvacSystemFunctionListData = castData[model.HvacSystemFunctionListDataType](data) - case model.FunctionTypeHvacSystemFunctionOperationModeRelationListData: - result.HvacSystemFunctionOperationModeRelationListData = castData[model.HvacSystemFunctionOperationModeRelationListDataType](data) - case model.FunctionTypeHvacSystemFunctionPowerSequenceRelationListData: - result.HvacSystemFunctionPowerSequenceRelationListData = castData[model.HvacSystemFunctionPowerSequenceRelationListDataType](data) - case model.FunctionTypeHvacSystemFunctionSetPointRelationListData: - result.HvacSystemFunctionSetPointRelationListData = castData[model.HvacSystemFunctionSetpointRelationListDataType](data) - case model.FunctionTypeIdentificationListData: - result.IdentificationListData = castData[model.IdentificationListDataType](data) - case model.FunctionTypeIncentiveDescriptionListData: - result.IncentiveDescriptionListData = castData[model.IncentiveDescriptionListDataType](data) - case model.FunctionTypeIncentiveListData: - result.IncentiveListData = castData[model.IncentiveListDataType](data) - case model.FunctionTypeIncentiveTableConstraintsData: - result.IncentiveTableConstraintsData = castData[model.IncentiveTableConstraintsDataType](data) - case model.FunctionTypeIncentiveTableData: - result.IncentiveTableData = castData[model.IncentiveTableDataType](data) - case model.FunctionTypeIncentiveTableDescriptionData: - result.IncentiveTableDescriptionData = castData[model.IncentiveTableDescriptionDataType](data) - case model.FunctionTypeLoadControlEventListData: - result.LoadControlEventListData = castData[model.LoadControlEventListDataType](data) - case model.FunctionTypeLoadControlLimitConstraintsListData: - result.LoadControlLimitConstraintsListData = castData[model.LoadControlLimitConstraintsListDataType](data) - case model.FunctionTypeLoadControlLimitDescriptionListData: - result.LoadControlLimitDescriptionListData = castData[model.LoadControlLimitDescriptionListDataType](data) - case model.FunctionTypeLoadControlLimitListData: - result.LoadControlLimitListData = castData[model.LoadControlLimitListDataType](data) - case model.FunctionTypeLoadControlNodeData: - result.LoadControlNodeData = castData[model.LoadControlNodeDataType](data) - case model.FunctionTypeLoadControlStateListData: - result.LoadControlStateListData = castData[model.LoadControlStateListDataType](data) - case model.FunctionTypeMeasurementConstraintsListData: - result.MeasurementConstraintsListData = castData[model.MeasurementConstraintsListDataType](data) - case model.FunctionTypeMeasurementDescriptionListData: - result.MeasurementDescriptionListData = castData[model.MeasurementDescriptionListDataType](data) - case model.FunctionTypeMeasurementListData: - result.MeasurementListData = castData[model.MeasurementListDataType](data) - case model.FunctionTypeMeasurementSeriesListData: - result.MeasurementSeriesListData = castData[model.MeasurementSeriesListDataType](data) - case model.FunctionTypeMeasurementThresholdRelationListData: - result.MeasurementThresholdRelationListData = castData[model.MeasurementThresholdRelationListDataType](data) - case model.FunctionTypeMessagingListData: - result.MessagingListData = castData[model.MessagingListDataType](data) - case model.FunctionTypeNetworkManagementAbortCall: - result.NetworkManagementAbortCall = castData[model.NetworkManagementAbortCallType](data) - case model.FunctionTypeNetworkManagementAddNodeCall: - result.NetworkManagementAddNodeCall = castData[model.NetworkManagementAddNodeCallType](data) - case model.FunctionTypeNetworkManagementDeviceDescriptionListData: - result.NetworkManagementDeviceDescriptionListData = castData[model.NetworkManagementDeviceDescriptionListDataType](data) - case model.FunctionTypeNetworkManagementDiscoverCall: - result.NetworkManagementDiscoverCall = castData[model.NetworkManagementDiscoverCallType](data) - case model.FunctionTypeNetworkManagementEntityDescriptionListData: - result.NetworkManagementEntityDescriptionListData = castData[model.NetworkManagementEntityDescriptionListDataType](data) - case model.FunctionTypeNetworkManagementFeatureDescriptionListData: - result.NetworkManagementFeatureDescriptionListData = castData[model.NetworkManagementFeatureDescriptionListDataType](data) - case model.FunctionTypeNetworkManagementJoiningModeData: - result.NetworkManagementJoiningModeData = castData[model.NetworkManagementJoiningModeDataType](data) - case model.FunctionTypeNetworkManagementModifyNodeCall: - result.NetworkManagementModifyNodeCall = castData[model.NetworkManagementModifyNodeCallType](data) - case model.FunctionTypeNetworkManagementProcessStateData: - result.NetworkManagementProcessStateData = castData[model.NetworkManagementProcessStateDataType](data) - case model.FunctionTypeNetworkManagementRemoveNodeCall: - result.NetworkManagementRemoveNodeCall = castData[model.NetworkManagementRemoveNodeCallType](data) - case model.FunctionTypeNetworkManagementReportCandidateData: - result.NetworkManagementReportCandidateData = castData[model.NetworkManagementReportCandidateDataType](data) - case model.FunctionTypeNetworkManagementScanNetworkCall: - result.NetworkManagementScanNetworkCall = castData[model.NetworkManagementScanNetworkCallType](data) - case model.FunctionTypeOperatingConstraintsDurationListData: - result.OperatingConstraintsDurationListData = castData[model.OperatingConstraintsDurationListDataType](data) - case model.FunctionTypeOperatingConstraintsInterruptListData: - result.OperatingConstraintsInterruptListData = castData[model.OperatingConstraintsInterruptListDataType](data) - case model.FunctionTypeOperatingConstraintsPowerDescriptionListData: - result.OperatingConstraintsPowerDescriptionListData = castData[model.OperatingConstraintsPowerDescriptionListDataType](data) - case model.FunctionTypeOperatingConstraintsPowerLevelListData: - result.OperatingConstraintsPowerLevelListData = castData[model.OperatingConstraintsPowerLevelListDataType](data) - case model.FunctionTypeOperatingConstraintsPowerRangeListData: - result.OperatingConstraintsPowerRangeListData = castData[model.OperatingConstraintsPowerRangeListDataType](data) - case model.FunctionTypeOperatingConstraintsResumeImplicationListData: - result.OperatingConstraintsResumeImplicationListData = castData[model.OperatingConstraintsResumeImplicationListDataType](data) - case model.FunctionTypePowerSequenceAlternativesRelationListData: - result.PowerSequenceAlternativesRelationListData = castData[model.PowerSequenceAlternativesRelationListDataType](data) - case model.FunctionTypePowerSequenceDescriptionListData: - result.PowerSequenceDescriptionListData = castData[model.PowerSequenceDescriptionListDataType](data) - case model.FunctionTypePowerSequenceNodeScheduleInformationData: - result.PowerSequenceNodeScheduleInformationData = castData[model.PowerSequenceNodeScheduleInformationDataType](data) - case model.FunctionTypePowerSequencePriceCalculationRequestCall: - result.PowerSequencePriceCalculationRequestCall = castData[model.PowerSequencePriceCalculationRequestCallType](data) - case model.FunctionTypePowerSequencePriceListData: - result.PowerSequencePriceListData = castData[model.PowerSequencePriceListDataType](data) - case model.FunctionTypePowerSequenceScheduleConfigurationRequestCall: - result.PowerSequenceScheduleConfigurationRequestCall = castData[model.PowerSequenceScheduleConfigurationRequestCallType](data) - case model.FunctionTypePowerSequenceScheduleConstraintsListData: - result.PowerSequenceScheduleConstraintsListData = castData[model.PowerSequenceScheduleConstraintsListDataType](data) - case model.FunctionTypePowerSequenceScheduleListData: - result.PowerSequenceScheduleListData = castData[model.PowerSequenceScheduleListDataType](data) - case model.FunctionTypePowerSequenceSchedulePreferenceListData: - result.PowerSequenceSchedulePreferenceListData = castData[model.PowerSequenceSchedulePreferenceListDataType](data) - case model.FunctionTypePowerSequenceStateListData: - result.PowerSequenceStateListData = castData[model.PowerSequenceStateListDataType](data) - case model.FunctionTypePowerTimeSlotScheduleConstraintsListData: - result.PowerTimeSlotScheduleConstraintsListData = castData[model.PowerTimeSlotScheduleConstraintsListDataType](data) - case model.FunctionTypePowerTimeSlotScheduleListData: - result.PowerTimeSlotScheduleListData = castData[model.PowerTimeSlotScheduleListDataType](data) - case model.FunctionTypePowerTimeSlotValueListData: - result.PowerTimeSlotValueListData = castData[model.PowerTimeSlotValueListDataType](data) - case model.FunctionTypeResultData: - result.ResultData = castData[model.ResultDataType](data) - case model.FunctionTypeSensingDescriptionData: - result.SensingDescriptionData = castData[model.SensingDescriptionDataType](data) - case model.FunctionTypeSensingListData: - result.SensingListData = castData[model.SensingListDataType](data) - case model.FunctionTypeSessionIdentificationListData: - result.SessionIdentificationListData = castData[model.SessionIdentificationListDataType](data) - case model.FunctionTypeSessionMeasurementRelationListData: - result.SessionMeasurementRelationListData = castData[model.SessionMeasurementRelationListDataType](data) - case model.FunctionTypeSetpointConstraintsListData: - result.SetpointConstraintsListData = castData[model.SetpointConstraintsListDataType](data) - case model.FunctionTypeSetpointDescriptionListData: - result.SetpointDescriptionListData = castData[model.SetpointDescriptionListDataType](data) - case model.FunctionTypeSetpointListData: - result.SetpointListData = castData[model.SetpointListDataType](data) - case model.FunctionTypeSmartEnergyManagementPsConfigurationRequestCall: - result.SmartEnergyManagementPsConfigurationRequestCall = castData[model.SmartEnergyManagementPsConfigurationRequestCallType](data) - case model.FunctionTypeSmartEnergyManagementPsData: - result.SmartEnergyManagementPsData = castData[model.SmartEnergyManagementPsDataType](data) - case model.FunctionTypeSmartEnergyManagementPsPriceCalculationRequestCall: - result.SmartEnergyManagementPsPriceCalculationRequestCall = castData[model.SmartEnergyManagementPsPriceCalculationRequestCallType](data) - case model.FunctionTypeSmartEnergyManagementPsPriceData: - result.SmartEnergyManagementPsPriceData = castData[model.SmartEnergyManagementPsPriceDataType](data) - case model.FunctionTypeSpecificationVersionListData: - result.SpecificationVersionListData = castData[model.SpecificationVersionListDataType](data) - case model.FunctionTypeStateInformationListData: - result.StateInformationListData = castData[model.StateInformationListDataType](data) - case model.FunctionTypeSupplyConditionListData: - result.SupplyConditionListData = castData[model.SupplyConditionListDataType](data) - case model.FunctionTypeSupplyConditionThresholdRelationListData: - result.SupplyConditionThresholdRelationListData = castData[model.SupplyConditionThresholdRelationListDataType](data) - case model.FunctionTypeTariffBoundaryRelationListData: - result.TariffBoundaryRelationListData = castData[model.TariffBoundaryRelationListDataType](data) - case model.FunctionTypeTariffDescriptionListData: - result.TariffDescriptionListData = castData[model.TariffDescriptionListDataType](data) - case model.FunctionTypeTariffListData: - result.TariffListData = castData[model.TariffListDataType](data) - case model.FunctionTypeTariffOverallConstraintsData: - result.TariffOverallConstraintsData = castData[model.TariffOverallConstraintsDataType](data) - case model.FunctionTypeTariffTierRelationListData: - result.TariffTierRelationListData = castData[model.TariffTierRelationListDataType](data) - case model.FunctionTypeTaskManagementJobDescriptionListData: - result.TaskManagementJobDescriptionListData = castData[model.TaskManagementJobDescriptionListDataType](data) - case model.FunctionTypeTaskManagementJobListData: - result.TaskManagementJobListData = castData[model.TaskManagementJobListDataType](data) - case model.FunctionTypeTaskManagementJobRelationListData: - result.TaskManagementJobRelationListData = castData[model.TaskManagementJobRelationListDataType](data) - case model.FunctionTypeTaskManagementOverviewData: - result.TaskManagementOverviewData = castData[model.TaskManagementOverviewDataType](data) - case model.FunctionTypeThresholdConstraintsListData: - result.ThresholdConstraintsListData = castData[model.ThresholdConstraintsListDataType](data) - case model.FunctionTypeThresholdDescriptionListData: - result.ThresholdDescriptionListData = castData[model.ThresholdDescriptionListDataType](data) - case model.FunctionTypeThresholdListData: - result.ThresholdListData = castData[model.ThresholdListDataType](data) - case model.FunctionTypeTierBoundaryDescriptionListData: - result.TierBoundaryDescriptionListData = castData[model.TierBoundaryDescriptionListDataType](data) - case model.FunctionTypeTierBoundaryListData: - result.TierBoundaryListData = castData[model.TierBoundaryListDataType](data) - case model.FunctionTypeTierDescriptionListData: - result.TierDescriptionListData = castData[model.TierDescriptionListDataType](data) - case model.FunctionTypeTierIncentiveRelationListData: - result.TierIncentiveRelationListData = castData[model.TierIncentiveRelationListDataType](data) - case model.FunctionTypeTierListData: - result.TierListData = castData[model.TierListDataType](data) - case model.FunctionTypeTimeDistributorData: - result.TimeDistributorData = castData[model.TimeDistributorDataType](data) - case model.FunctionTypeTimeDistributorEnquiryCall: - result.TimeDistributorEnquiryCall = castData[model.TimeDistributorEnquiryCallType](data) - case model.FunctionTypeTimeInformationData: - result.TimeInformationData = castData[model.TimeInformationDataType](data) - case model.FunctionTypeTimePrecisionData: - result.TimePrecisionData = castData[model.TimePrecisionDataType](data) - case model.FunctionTypeTimeSeriesConstraintsListData: - result.TimeSeriesConstraintsListData = castData[model.TimeSeriesConstraintsListDataType](data) - case model.FunctionTypeTimeSeriesDescriptionListData: - result.TimeSeriesDescriptionListData = castData[model.TimeSeriesDescriptionListDataType](data) - case model.FunctionTypeTimeSeriesListData: - result.TimeSeriesListData = castData[model.TimeSeriesListDataType](data) - case model.FunctionTypeTimeTableConstraintsListData: - result.TimeTableConstraintsListData = castData[model.TimeTableConstraintsListDataType](data) - case model.FunctionTypeTimeTableDescriptionListData: - result.TimeTableDescriptionListData = castData[model.TimeTableDescriptionListDataType](data) - case model.FunctionTypeTimeTableListData: - result.TimeTableListData = castData[model.TimeTableListDataType](data) - // add more model types here - } + result.SetDataForFunction(function, data) return result } - -func castData[D, S any](data *S) *D { - if data == nil { - return new(D) - } - return any(data).(*D) -} diff --git a/spine/model/commandframe_additions.go b/spine/model/commandframe_additions.go index 853e8a1c..2958cb94 100644 --- a/spine/model/commandframe_additions.go +++ b/spine/model/commandframe_additions.go @@ -59,6 +59,52 @@ func (f *FilterData) SelectorMatch(item any) bool { return true } +// Get the field for a given functionType +func (f *FilterType) SetDataForFunction(tagType EEBusTagTypeType, fct FunctionType, data any) { + v := reflect.ValueOf(*f) + dv := reflect.ValueOf(f).Elem() + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + if field.Kind() != reflect.Ptr { + continue + } + + sf := v.Type().Field(i) + // Exclude the generic fields + if sf.Name == "CmdControl" || sf.Name == "FilterId" { + continue + } + + eebusTags := EEBusTags(sf) + function, exists := eebusTags[EEBusTagFunction] + if !exists { + continue + } + typ, exists := eebusTags[EEBusTagType] + if !exists || len(typ) == 0 { + continue + } + if typ != string(tagType) { + continue + } + + if fct != FunctionType(function) { + continue + } + + n := v.Type().Field(i).Name + ff := dv.FieldByName(n) + + if !ff.CanSet() { + break + } + + dataV := reflect.ValueOf(data) + dataC := dataV.Convert(ff.Type()) + ff.Set(dataC) + } +} + // Get the data and some meta data for the current value func (f *FilterType) Data() (*FilterData, error) { var elements any = nil @@ -122,6 +168,45 @@ type CmdData struct { Value any } +// Get the field for a given functionType +func (cmd *CmdType) SetDataForFunction(fct FunctionType, data any) { + v := reflect.ValueOf(*cmd) + dv := reflect.ValueOf(cmd).Elem() + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + if f.Kind() != reflect.Ptr { + continue + } + + sf := v.Type().Field(i) + // Exclude the CmdOptionGroup fields + if sf.Name == "Function" || sf.Name == "Filter" { + continue + } + + eebusTags := EEBusTags(sf) + function, exists := eebusTags[EEBusTagFunction] + if !exists { + continue + } + + if fct != FunctionType(function) { + continue + } + + n := v.Type().Field(i).Name + ff := dv.FieldByName(n) + + if !ff.CanSet() { + break + } + + dataV := reflect.ValueOf(data) + dataC := dataV.Convert(ff.Type()) + ff.Set(dataC) + } +} + // Get the data and some meta data of the current value func (cmd *CmdType) Data() (*CmdData, error) { v := reflect.ValueOf(*cmd) From 64c207f4e1ada0c8fb7d7f2e86adf9b23409d541 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 12:47:15 +0100 Subject: [PATCH 078/240] Add missing function --- spine/function_data_factory.go | 1 + spine/function_data_factory_test.go | 4 ++-- spine/model/commondatatypes.go | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spine/function_data_factory.go b/spine/function_data_factory.go index 3e4102b0..f2a99328 100644 --- a/spine/function_data_factory.go +++ b/spine/function_data_factory.go @@ -88,6 +88,7 @@ func CreateFunctionData[F any](featureType model.FeatureTypeType) []F { createFunctionData[model.ElectricalConnectionParameterDescriptionListDataType, F](model.FunctionTypeElectricalConnectionParameterDescriptionListData), createFunctionData[model.ElectricalConnectionPermittedValueSetListDataType, F](model.FunctionTypeElectricalConnectionPermittedValueSetListData), createFunctionData[model.ElectricalConnectionStateListDataType, F](model.FunctionTypeElectricalConnectionStateListData), + createFunctionData[model.ElectricalConnectionCharacteristicDataType, F](model.FunctionTypeElectricalConnectionCharacteristicListData), createFunctionData[model.ElectricalConnectionCharacteristicListDataType, F](model.FunctionTypeElectricalConnectionCharacteristicListData), }...) } diff --git a/spine/function_data_factory_test.go b/spine/function_data_factory_test.go index ef9f35f0..af63826d 100644 --- a/spine/function_data_factory_test.go +++ b/spine/function_data_factory_test.go @@ -31,7 +31,7 @@ func TestFunctionDataFactory_FunctionData(t *testing.T) { assert.IsType(t, &FunctionDataImpl[model.DeviceDiagnosisHeartbeatDataType]{}, result[1]) result = CreateFunctionData[FunctionData](model.FeatureTypeTypeElectricalConnection) - assert.Equal(t, 5, len(result)) + assert.Equal(t, 6, len(result)) assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionDescriptionListDataType]{}, result[0]) assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionParameterDescriptionListDataType]{}, result[1]) assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionPermittedValueSetListDataType]{}, result[2]) @@ -75,7 +75,7 @@ func TestFunctionDataFactory_FunctionData(t *testing.T) { assert.IsType(t, &FunctionDataImpl[model.TimeSeriesListDataType]{}, result[2]) result = CreateFunctionData[FunctionData](model.FeatureTypeTypeGeneric) - assert.Equal(t, 123, len(result)) + assert.Equal(t, 124, len(result)) } func TestFunctionDataFactory_FunctionDataCmd(t *testing.T) { diff --git a/spine/model/commondatatypes.go b/spine/model/commondatatypes.go index a7606ae8..f7d6c958 100644 --- a/spine/model/commondatatypes.go +++ b/spine/model/commondatatypes.go @@ -950,6 +950,7 @@ const ( FunctionTypeBillListData FunctionType = "billListData" FunctionTypeIdentificationListData FunctionType = "identificationListData" FunctionTypeMeasurementSeriesListData FunctionType = "measurementSeriesListData" + FunctionTypeElectricalConnectionCharacteristicData FunctionType = "electricalConnectionCharacteristicData" FunctionTypeElectricalConnectionCharacteristicListData FunctionType = "electricalConnectionCharacteristicListData" FunctionTypeStateInformationListData FunctionType = "stateInformationListData" ) From f2e291813cdc5d8ee46edc4f8aacf5598f3e9b8a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 13:21:23 +0100 Subject: [PATCH 079/240] Fix handling of nil --- spine/model/commandframe_additions.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/spine/model/commandframe_additions.go b/spine/model/commandframe_additions.go index 2958cb94..b2b43630 100644 --- a/spine/model/commandframe_additions.go +++ b/spine/model/commandframe_additions.go @@ -99,9 +99,16 @@ func (f *FilterType) SetDataForFunction(tagType EEBusTagTypeType, fct FunctionTy break } + if data == nil || (reflect.ValueOf(data).Kind() == reflect.Ptr && reflect.ValueOf(data).IsNil()) { + typ := reflect.TypeOf(data).Elem() + ff.Set(reflect.New(typ)) + return + } + dataV := reflect.ValueOf(data) dataC := dataV.Convert(ff.Type()) ff.Set(dataC) + return } } @@ -201,9 +208,16 @@ func (cmd *CmdType) SetDataForFunction(fct FunctionType, data any) { break } + if data == nil || (reflect.ValueOf(data).Kind() == reflect.Ptr && reflect.ValueOf(data).IsNil()) { + typ := reflect.TypeOf(data).Elem() + ff.Set(reflect.New(typ)) + return + } + dataV := reflect.ValueOf(data) dataC := dataV.Convert(ff.Type()) ff.Set(dataC) + return } } From a3c6c3015c552a8e89920a90d688ecba40c993ba Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 13:26:25 +0100 Subject: [PATCH 080/240] Update github actions --- .github/workflows/default.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 8e4007c5..7cad577e 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -18,10 +18,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: go-version: ^1.18 From 1940014f21bed072dce990ec4187e80b17b76f0d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 13:27:42 +0100 Subject: [PATCH 081/240] Upgrade go modules --- go.mod | 12 ++++++------ go.sum | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 3206f2d2..389c5597 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,14 @@ go 1.18 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/go-cmp v0.6.0 - github.com/miekg/dns v1.1.56 // indirect + github.com/miekg/dns v1.1.57 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/plural v1.4.1 // indirect github.com/stretchr/objx v0.5.1 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/tools v0.16.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -21,7 +21,7 @@ require ( github.com/ahmetb/go-linq/v3 v3.2.0 github.com/godbus/dbus/v5 v5.1.0 github.com/golang/mock v1.6.0 - github.com/gorilla/websocket v1.5.0 + github.com/gorilla/websocket v1.5.1 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed github.com/rickb777/date v1.20.5 diff --git a/go.sum b/go.sum index bac29a91..049ccd1f 100644 --- a/go.sum +++ b/go.sum @@ -13,12 +13,16 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed h1:AMm8KKtfeEhUlj45DYJBSMW2VcLO1Tss3jaMUqb+VvE= github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -51,6 +55,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -61,6 +67,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -80,6 +88,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -101,6 +111,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 1bdad4fa48f4c8a00c0717aba6d9edea5a5433b7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 18:21:57 +0100 Subject: [PATCH 082/240] Fix handling of nil data and add more tests --- spine/model/commandframe_additions.go | 12 +++- spine/model/commandframe_additions_test.go | 81 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/spine/model/commandframe_additions.go b/spine/model/commandframe_additions.go index b2b43630..a8fcaf7b 100644 --- a/spine/model/commandframe_additions.go +++ b/spine/model/commandframe_additions.go @@ -61,6 +61,10 @@ func (f *FilterData) SelectorMatch(item any) bool { // Get the field for a given functionType func (f *FilterType) SetDataForFunction(tagType EEBusTagTypeType, fct FunctionType, data any) { + if data == nil || reflect.ValueOf(data).Kind() != reflect.Ptr { + return + } + v := reflect.ValueOf(*f) dv := reflect.ValueOf(f).Elem() for i := 0; i < v.NumField(); i++ { @@ -99,7 +103,7 @@ func (f *FilterType) SetDataForFunction(tagType EEBusTagTypeType, fct FunctionTy break } - if data == nil || (reflect.ValueOf(data).Kind() == reflect.Ptr && reflect.ValueOf(data).IsNil()) { + if reflect.ValueOf(data).IsNil() { typ := reflect.TypeOf(data).Elem() ff.Set(reflect.New(typ)) return @@ -177,6 +181,10 @@ type CmdData struct { // Get the field for a given functionType func (cmd *CmdType) SetDataForFunction(fct FunctionType, data any) { + if data == nil || reflect.ValueOf(data).Kind() != reflect.Ptr { + return + } + v := reflect.ValueOf(*cmd) dv := reflect.ValueOf(cmd).Elem() for i := 0; i < v.NumField(); i++ { @@ -208,7 +216,7 @@ func (cmd *CmdType) SetDataForFunction(fct FunctionType, data any) { break } - if data == nil || (reflect.ValueOf(data).Kind() == reflect.Ptr && reflect.ValueOf(data).IsNil()) { + if reflect.ValueOf(data).IsNil() { typ := reflect.TypeOf(data).Elem() ff.Set(reflect.New(typ)) return diff --git a/spine/model/commandframe_additions_test.go b/spine/model/commandframe_additions_test.go index 60241907..ca423335 100644 --- a/spine/model/commandframe_additions_test.go +++ b/spine/model/commandframe_additions_test.go @@ -4,9 +4,75 @@ import ( "testing" "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) +func TestFilterType_Selector_Data(t *testing.T) { + data := &model.ElectricalConnectionDescriptionListDataSelectorsType{ + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + } + + sut := &model.FilterType{ + ElectricalConnectionDescriptionListDataSelectors: data, + } + + // Act + cmdData, err := sut.Data() + assert.Nil(t, err) + assert.NotNil(t, cmdData) + assert.Equal(t, model.FunctionTypeElectricalConnectionDescriptionListData, *cmdData.Function) + assert.Equal(t, data, cmdData.Selector) +} + +func TestFilterType_Selector_SetDataForFunction(t *testing.T) { + cmd := model.FilterType{} + cmd.SetDataForFunction(model.EEBusTagTypeTypeSelector, model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionListDataSelectorsType{}) + assert.NotNil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) + + cmd = model.FilterType{} + cmd.SetDataForFunction(model.EEBusTagTypeTypeSelector, model.FunctionTypeElectricalConnectionDescriptionListData, nil) + assert.Nil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) + + var test *model.ElectricalConnectionDescriptionListDataSelectorsType + cmd = model.FilterType{} + cmd.SetDataForFunction(model.EEBusTagTypeTypeSelector, model.FunctionTypeElectricalConnectionDescriptionListData, test) + assert.NotNil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) +} + +func TestFilterType_Elements_Data(t *testing.T) { + data := &model.ElectricalConnectionDescriptionDataElementsType{ + ElectricalConnectionId: util.Ptr(model.ElementTagType{}), + } + + sut := &model.FilterType{ + ElectricalConnectionDescriptionDataElements: data, + } + + // Act + cmdData, err := sut.Data() + assert.Nil(t, err) + assert.NotNil(t, cmdData) + assert.Equal(t, model.FunctionTypeElectricalConnectionDescriptionListData, *cmdData.Function) + assert.Equal(t, data, cmdData.Elements) +} + +func TestFilterType_Elements_SetDataForFunction(t *testing.T) { + cmd := model.FilterType{} + cmd.SetDataForFunction(model.EEbusTagTypeTypeElements, model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionDataElementsType{}) + assert.NotNil(t, cmd.ElectricalConnectionDescriptionDataElements) + + cmd = model.FilterType{} + cmd.SetDataForFunction(model.EEbusTagTypeTypeElements, model.FunctionTypeElectricalConnectionDescriptionListData, nil) + assert.Nil(t, cmd.ElectricalConnectionDescriptionDataElements) + + var test *model.ElectricalConnectionDescriptionDataElementsType + cmd = model.FilterType{} + cmd.SetDataForFunction(model.EEbusTagTypeTypeElements, model.FunctionTypeElectricalConnectionDescriptionListData, test) + assert.NotNil(t, cmd.ElectricalConnectionDescriptionDataElements) +} + func TestCmdType_Data(t *testing.T) { data := &model.NodeManagementDetailedDiscoveryDataType{ SpecificationVersionList: &model.NodeManagementSpecificationVersionListType{[]model.SpecificationVersionDataType{model.SpecificationVersionDataType("dummy")}}, @@ -25,6 +91,21 @@ func TestCmdType_Data(t *testing.T) { assert.Equal(t, data, cmdData.Value) } +func TestCmdType_SetDataForFunction(t *testing.T) { + cmd := model.CmdType{} + cmd.SetDataForFunction(model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionListDataType{}) + assert.NotNil(t, cmd.ElectricalConnectionDescriptionListData) + + cmd = model.CmdType{} + cmd.SetDataForFunction(model.FunctionTypeElectricalConnectionDescriptionListData, nil) + assert.Nil(t, cmd.ElectricalConnectionDescriptionListData) + + var test *model.ElectricalConnectionDescriptionListDataType + cmd = model.CmdType{} + cmd.SetDataForFunction(model.FunctionTypeElectricalConnectionDescriptionListData, test) + assert.NotNil(t, cmd.ElectricalConnectionDescriptionListData) +} + func TestCmdType_ExtractFilter_NoFilter(t *testing.T) { sut := &model.CmdType{ NodeManagementDetailedDiscoveryData: &model.NodeManagementDetailedDiscoveryDataType{}, From a79cf5fc6ae685cd43e731e3dca1ee1e3623aa85 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 18:42:42 +0100 Subject: [PATCH 083/240] Add more nodemanagement tests --- spine/nodemanagement_binding.go | 9 ++ spine/nodemanagement_subscription.go | 9 ++ spine/nodemanagement_test.go | 146 ++++++++++++++++++--------- 3 files changed, 115 insertions(+), 49 deletions(-) diff --git a/spine/nodemanagement_binding.go b/spine/nodemanagement_binding.go index 484734b2..68d9eaac 100644 --- a/spine/nodemanagement_binding.go +++ b/spine/nodemanagement_binding.go @@ -18,6 +18,15 @@ func NewNodeManagementBindingRequestCallType(clientAddress *model.FeatureAddress } } +func NewNodeManagementBindingDeleteCallType(clientAddress *model.FeatureAddressType, serverAddress *model.FeatureAddressType) *model.NodeManagementBindingDeleteCallType { + return &model.NodeManagementBindingDeleteCallType{ + BindingDelete: &model.BindingManagementDeleteCallType{ + ClientAddress: clientAddress, + ServerAddress: serverAddress, + }, + } +} + // route bindings request calls to the appropriate feature implementation and add the bindings to the current list func (r *NodeManagementImpl) processReadBindingData(message *Message) error { diff --git a/spine/nodemanagement_subscription.go b/spine/nodemanagement_subscription.go index 5d215cd9..181ef83e 100644 --- a/spine/nodemanagement_subscription.go +++ b/spine/nodemanagement_subscription.go @@ -18,6 +18,15 @@ func NewNodeManagementSubscriptionRequestCallType(clientAddress *model.FeatureAd } } +func NewNodeManagementSubscriptionDeleteCallType(clientAddress *model.FeatureAddressType, serverAddress *model.FeatureAddressType, featureType model.FeatureTypeType) *model.NodeManagementSubscriptionDeleteCallType { + return &model.NodeManagementSubscriptionDeleteCallType{ + SubscriptionDelete: &model.SubscriptionManagementDeleteCallType{ + ClientAddress: clientAddress, + ServerAddress: serverAddress, + }, + } +} + // route subscription request calls to the appropriate feature implementation and add the subscription to the current list func (r *NodeManagementImpl) processReadSubscriptionData(message *Message) error { diff --git a/spine/nodemanagement_test.go b/spine/nodemanagement_test.go index 148f83a8..afd0d773 100644 --- a/spine/nodemanagement_test.go +++ b/spine/nodemanagement_test.go @@ -11,35 +11,94 @@ import ( "github.com/stretchr/testify/mock" ) -func TestNodemanagement_SubscriptionRequestCall(t *testing.T) { +func TestNodemanagement_BindingCalls(t *testing.T) { + const bindingEntityId uint = 1 + const featureType = model.FeatureTypeTypeLoadControl - // const serverName = "Server" - // const clientName = "Client" + senderMock := mocks.NewSender(t) + + serverFeature := createLocalDeviceAndFeature(bindingEntityId, featureType, model.RoleTypeServer) + clientFeature := createRemoteDeviceAndFeature(bindingEntityId, featureType, model.RoleTypeClient, senderMock) + + senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + cmd := args.Get(2).(model.CmdType) + assert.Equal(t, 1, len(cmd.NodeManagementBindingData.BindingEntry)) + assert.True(t, reflect.DeepEqual(cmd.NodeManagementBindingData.BindingEntry[0].ClientAddress, clientFeature.Address())) + assert.True(t, reflect.DeepEqual(cmd.NodeManagementBindingData.BindingEntry[0].ServerAddress, serverFeature.Address())) + }).Return(nil).Once() + + requestMsg := spine.Message{ + Cmd: model.CmdType{ + NodeManagementBindingRequestCall: spine.NewNodeManagementBindingRequestCallType( + clientFeature.Address(), serverFeature.Address(), featureType), + }, + CmdClassifier: model.CmdClassifierTypeCall, + FeatureRemote: clientFeature, + } + + sut := spine.NewNodeManagementImpl(0, serverFeature.Entity()) + + // Act + err := sut.HandleMessage(&requestMsg) + if assert.Nil(t, err) { + + dataMsg := spine.Message{ + Cmd: model.CmdType{ + NodeManagementBindingData: &model.NodeManagementBindingDataType{}, + }, + CmdClassifier: model.CmdClassifierTypeCall, + FeatureRemote: clientFeature, + } + err = sut.HandleMessage(&dataMsg) + assert.Nil(t, err) + } + + senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + cmd := args.Get(2).(model.CmdType) + assert.Equal(t, 0, len(cmd.NodeManagementBindingData.BindingEntry)) + }).Return(nil).Once() + + deleteMsg := spine.Message{ + Cmd: model.CmdType{ + NodeManagementBindingDeleteCall: spine.NewNodeManagementBindingDeleteCallType( + clientFeature.Address(), serverFeature.Address()), + }, + CmdClassifier: model.CmdClassifierTypeCall, + FeatureRemote: clientFeature, + } + + // Act + err = sut.HandleMessage(&deleteMsg) + if assert.Nil(t, err) { + dataMsg := spine.Message{ + Cmd: model.CmdType{ + NodeManagementBindingData: &model.NodeManagementBindingDataType{}, + }, + CmdClassifier: model.CmdClassifierTypeCall, + FeatureRemote: clientFeature, + } + err = sut.HandleMessage(&dataMsg) + assert.Nil(t, err) + } +} + +func TestNodemanagement_SubscriptionCalls(t *testing.T) { const subscriptionEntityId uint = 1 - //const subscriptionFeatureId uint = 2 const featureType = model.FeatureTypeTypeDeviceClassification senderMock := mocks.NewSender(t) - //localDevice := NewDeviceLocalImpl(model.AddressDeviceType("server")) - serverFeature := createLocalDeviceAndFeature(subscriptionEntityId, featureType, model.RoleTypeServer) clientFeature := createRemoteDeviceAndFeature(subscriptionEntityId, featureType, model.RoleTypeClient, senderMock) - // serverAddress := featureAddress(serverName, subscriptionEntityId, subscriptionFeatureId) - // clientAddress := featureAddress(clientName, subscriptionEntityId, subscriptionFeatureId) senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { cmd := args.Get(2).(model.CmdType) assert.Equal(t, 1, len(cmd.NodeManagementSubscriptionData.SubscriptionEntry)) assert.True(t, reflect.DeepEqual(cmd.NodeManagementSubscriptionData.SubscriptionEntry[0].ClientAddress, clientFeature.Address())) assert.True(t, reflect.DeepEqual(cmd.NodeManagementSubscriptionData.SubscriptionEntry[0].ServerAddress, serverFeature.Address())) - }).Return(nil).Once() - // serverFeatureMock := newFeatureLocalMock(serverAddress, model.RoleTypeServer, featureType, senderMock) - // clientFeatureMock := newFeatureRemoteMock(clientAddress, model.RoleTypeClient, featureType) - requestMsg := spine.Message{ Cmd: model.CmdType{ NodeManagementSubscriptionRequestCall: spine.NewNodeManagementSubscriptionRequestCallType( @@ -65,44 +124,33 @@ func TestNodemanagement_SubscriptionRequestCall(t *testing.T) { err = sut.HandleMessage(&dataMsg) assert.Nil(t, err) } -} - -// func newFeatureLocalMock(address *model.FeatureAddressType, role model.RoleType, ftype model.FeatureTypeType, sender spine.Sender) *mocks.FeatureLocal { -// deviceMock := new(mocks.DeviceLocal) -// entityMock := new(mocks.EntityLocal) -// featureMock := new(mocks.FeatureLocal) - -// deviceMock.On("FeatureByAddress", address).Return(featureMock) -// deviceMock.On("Address").Return(address.Device) -// deviceMock.On("Sender").Return(sender) -// entityMock.On("Device").Return(deviceMock) -// entityMock.On("Address").Return(address.Entity) - -// featureMock.On("Role").Return(role) -// featureMock.On("Type").Return(model.FeatureTypeEnumType(ftype)) -// featureMock.On("Device").Return(deviceMock) -// featureMock.On("Entity").Return(entityMock) - -// return featureMock -// } - -// func newFeatureRemoteMock(address *model.FeatureAddressType, role model.RoleType, ftype model.FeatureTypeType) *mocks.FeatureRemote { -// deviceMock := new(mocks.DeviceRemote) -// entityMock := new(mocks.EntityRemote) -// featureMock := new(mocks.FeatureRemote) - -// deviceMock.On("FeatureByAddress", address).Return(featureMock) -// deviceMock.On("Address").Return(address.Device) -// //deviceMock.On("Sender").Return(sender) + senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + cmd := args.Get(2).(model.CmdType) + assert.Equal(t, 0, len(cmd.NodeManagementSubscriptionData.SubscriptionEntry)) + }).Return(nil).Once() -// entityMock.On("Device").Return(deviceMock) -// entityMock.On("Address").Return(address.Entity) + deleteMsg := spine.Message{ + Cmd: model.CmdType{ + NodeManagementSubscriptionDeleteCall: spine.NewNodeManagementSubscriptionDeleteCallType( + clientFeature.Address(), serverFeature.Address(), featureType), + }, + CmdClassifier: model.CmdClassifierTypeCall, + FeatureRemote: clientFeature, + } -// featureMock.On("Role").Return(role) -// featureMock.On("Type").Return(model.FeatureTypeEnumType(ftype)) -// featureMock.On("Device").Return(deviceMock) -// featureMock.On("Entity").Return(entityMock) + // Act + err = sut.HandleMessage(&deleteMsg) + if assert.Nil(t, err) { -// return featureMock -// } + dataMsg := spine.Message{ + Cmd: model.CmdType{ + NodeManagementSubscriptionData: &model.NodeManagementSubscriptionDataType{}, + }, + CmdClassifier: model.CmdClassifierTypeCall, + FeatureRemote: clientFeature, + } + err = sut.HandleMessage(&dataMsg) + assert.Nil(t, err) + } +} From 4860fc73ada0626e64a75ddf27daea9eef988493 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 19:08:31 +0100 Subject: [PATCH 084/240] Rename mocks into test files --- service/hub.go | 2 +- service/mdns.go | 2 +- service/{mock_hub.go => mock_hub_test.go} | 0 service/{mock_mdns.go => mock_mdns_test.go} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename service/{mock_hub.go => mock_hub_test.go} (100%) rename service/{mock_mdns.go => mock_mdns_test.go} (100%) diff --git a/service/hub.go b/service/hub.go index a4ba7584..832bbb1c 100644 --- a/service/hub.go +++ b/service/hub.go @@ -40,7 +40,7 @@ var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ {min: 10, max: 20}, } -//go:generate mockgen -destination=mock_hub.go -package=service github.com/enbility/eebus-go/service ServiceProvider +//go:generate mockgen -destination=mock_hub_test.go -package=service github.com/enbility/eebus-go/service ServiceProvider // interface for reporting data from connectionsHub to the EEBUSService type ServiceProvider interface { diff --git a/service/mdns.go b/service/mdns.go index 0fa2684b..c377f70d 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -29,7 +29,7 @@ type MdnsEntry struct { Addresses []net.IP // mandatory } -//go:generate mockgen -destination=mock_mdns.go -package=service github.com/enbility/eebus-go/service MdnsSearch,MdnsService +//go:generate mockgen -destination=mock_mdns_test.go -package=service github.com/enbility/eebus-go/service MdnsSearch,MdnsService // implemented by hubConnection, used by mdns type MdnsSearch interface { diff --git a/service/mock_hub.go b/service/mock_hub_test.go similarity index 100% rename from service/mock_hub.go rename to service/mock_hub_test.go diff --git a/service/mock_mdns.go b/service/mock_mdns_test.go similarity index 100% rename from service/mock_mdns.go rename to service/mock_mdns_test.go From 3b455d793fd98bdafd83c5b5a649a14d0145cba3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 19:12:39 +0100 Subject: [PATCH 085/240] Move another mock to test --- ship/{mock_types.go => mock_types_test.go} | 0 ship/types.go | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename ship/{mock_types.go => mock_types_test.go} (100%) diff --git a/ship/mock_types.go b/ship/mock_types_test.go similarity index 100% rename from ship/mock_types.go rename to ship/mock_types_test.go diff --git a/ship/types.go b/ship/types.go index 47d62c52..65595197 100644 --- a/ship/types.go +++ b/ship/types.go @@ -110,7 +110,7 @@ const ( var shipInit []byte = []byte{model.MsgTypeInit, 0x00} -//go:generate mockgen -destination=mock_types.go -package=ship github.com/enbility/eebus-go/ship ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider +//go:generate mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider // interface for handling the actual remote device data connection // From cc5a74ead5ff4e91a66b0460aa6285c348210f90 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 31 Dec 2023 14:40:56 +0100 Subject: [PATCH 086/240] Remove bindings and subscriptions on disconnect --- spine/binding_manager.go | 43 ++++++++++++++++++++++++++++++ spine/binding_manager_test.go | 16 ++++++++++- spine/device_local.go | 12 ++++++++- spine/subscription_manager.go | 12 +++++++++ spine/subscription_manager_test.go | 17 +++++++++++- 5 files changed, 97 insertions(+), 3 deletions(-) diff --git a/spine/binding_manager.go b/spine/binding_manager.go index 7f3977fd..0512c234 100644 --- a/spine/binding_manager.go +++ b/spine/binding_manager.go @@ -15,6 +15,8 @@ import ( type BindingManager interface { AddBinding(remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error + RemoveBindingsForDevice(remoteDevice *DeviceRemoteImpl) + RemoveBindingsForEntity(remoteEntity *EntityRemoteImpl) Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry } @@ -148,6 +150,47 @@ func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCal return nil } +// Remove all existing bindings for a given remote device +func (c *BindingManagerImpl) RemoveBindingsForDevice(remoteDevice *DeviceRemoteImpl) { + if remoteDevice == nil { + return + } + + for _, entity := range remoteDevice.Entities() { + c.RemoveBindingsForEntity(entity) + } +} + +// Remove all existing bindings for a given remote device entity +func (c *BindingManagerImpl) RemoveBindingsForEntity(remoteEntity *EntityRemoteImpl) { + if remoteEntity == nil { + return + } + + c.mux.Lock() + defer c.mux.Unlock() + + var newBindingEntries []*BindingEntry + for _, item := range c.bindingEntries { + if !reflect.DeepEqual(item.clientFeature.Address().Entity, remoteEntity.Address().Entity) { + newBindingEntries = append(newBindingEntries, item) + continue + } + + clientFeature := remoteEntity.Feature(item.clientFeature.address.Feature) + payload := EventPayload{ + Ski: remoteEntity.Device().ski, + EventType: EventTypeBindingChange, + ChangeType: ElementChangeRemove, + Entity: remoteEntity, + Feature: clientFeature, + } + Events.Publish(payload) + } + + c.bindingEntries = newBindingEntries +} + func (c *BindingManagerImpl) Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry { var result []*BindingEntry diff --git a/spine/binding_manager_test.go b/spine/binding_manager_test.go index beceaa91..3abd39d8 100644 --- a/spine/binding_manager_test.go +++ b/spine/binding_manager_test.go @@ -60,10 +60,13 @@ func (suite *BindingManagerSuite) Test_Bindings() { err := bindingMgr.AddBinding(suite.remoteDevice, bindingRequest) assert.Nil(suite.T(), err) + subs := bindingMgr.Bindings(suite.remoteDevice) + assert.Equal(suite.T(), 1, len(subs)) + err = bindingMgr.AddBinding(suite.remoteDevice, bindingRequest) assert.NotNil(suite.T(), err) - subs := bindingMgr.Bindings(suite.remoteDevice) + subs = bindingMgr.Bindings(suite.remoteDevice) assert.Equal(suite.T(), 1, len(subs)) bindingDelete := model.BindingManagementDeleteCallType{ @@ -79,4 +82,15 @@ func (suite *BindingManagerSuite) Test_Bindings() { err = bindingMgr.RemoveBinding(bindingDelete, suite.remoteDevice) assert.NotNil(suite.T(), err) + + err = bindingMgr.AddBinding(suite.remoteDevice, bindingRequest) + assert.Nil(suite.T(), err) + + subs = bindingMgr.Bindings(suite.remoteDevice) + assert.Equal(suite.T(), 1, len(subs)) + + bindingMgr.RemoveBindingsForDevice(suite.remoteDevice) + + subs = bindingMgr.Bindings(suite.remoteDevice) + assert.Equal(suite.T(), 0, len(subs)) } diff --git a/spine/device_local.go b/spine/device_local.go index bced83a1..5c33057c 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -145,7 +145,17 @@ func (r *DeviceLocalImpl) RemoveRemoteDevice(ski string) { return } - // TODO: make sure subscriptions are removed, that way heartbeat should also be removed + // remove all subscriptions for this device + subscriptionMgr := r.SubscriptionManager() + subscriptionMgr.RemoveSubscriptionsForDevice(r.remoteDevices[ski]) + + // make sure Heartbeat Manager is up to date + r.HeartbeatManager().UpdateHeartbeatOnSubscriptions() + + // remove all bindings for this device + bindingMgr := r.BindingManager() + bindingMgr.RemoveBindingsForDevice(r.remoteDevices[ski]) + delete(r.remoteDevices, ski) // only unsubscribe if we don't have any remote devices left diff --git a/spine/subscription_manager.go b/spine/subscription_manager.go index b8254b1a..18ecf711 100644 --- a/spine/subscription_manager.go +++ b/spine/subscription_manager.go @@ -15,6 +15,7 @@ import ( type SubscriptionManager interface { AddSubscription(remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error + RemoveSubscriptionsForDevice(remoteDevice *DeviceRemoteImpl) RemoveSubscriptionsForEntity(remoteEntity *EntityRemoteImpl) Subscriptions(remoteDevice *DeviceRemoteImpl) []*SubscriptionEntry SubscriptionsOnFeature(featureAddress model.FeatureAddressType) []*SubscriptionEntry @@ -150,6 +151,17 @@ func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionMana return nil } +// Remove all existing subscriptions for a given remote device +func (c *SubscriptionManagerImpl) RemoveSubscriptionsForDevice(remoteDevice *DeviceRemoteImpl) { + if remoteDevice == nil { + return + } + + for _, entity := range remoteDevice.Entities() { + c.RemoveSubscriptionsForEntity(entity) + } +} + // Remove all existing subscriptions for a given remote device entity func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity *EntityRemoteImpl) { if remoteEntity == nil { diff --git a/spine/subscription_manager_test.go b/spine/subscription_manager_test.go index 562d506b..8561f3a9 100644 --- a/spine/subscription_manager_test.go +++ b/spine/subscription_manager_test.go @@ -60,10 +60,13 @@ func (suite *SubscriptionManagerSuite) Test_Subscriptions() { err := subMgr.AddSubscription(suite.remoteDevice, subscrRequest) assert.Nil(suite.T(), err) + subs := subMgr.Subscriptions(suite.remoteDevice) + assert.Equal(suite.T(), 1, len(subs)) + err = subMgr.AddSubscription(suite.remoteDevice, subscrRequest) assert.NotNil(suite.T(), err) - subs := subMgr.Subscriptions(suite.remoteDevice) + subs = subMgr.Subscriptions(suite.remoteDevice) assert.Equal(suite.T(), 1, len(subs)) subscrDelete := model.SubscriptionManagementDeleteCallType{ @@ -79,4 +82,16 @@ func (suite *SubscriptionManagerSuite) Test_Subscriptions() { err = subMgr.RemoveSubscription(subscrDelete, suite.remoteDevice) assert.NotNil(suite.T(), err) + + subMgr = suite.localDevice.SubscriptionManager() + err = subMgr.AddSubscription(suite.remoteDevice, subscrRequest) + assert.Nil(suite.T(), err) + + subs = subMgr.Subscriptions(suite.remoteDevice) + assert.Equal(suite.T(), 1, len(subs)) + + subMgr.RemoveSubscriptionsForDevice(suite.remoteDevice) + + subs = subMgr.Subscriptions(suite.remoteDevice) + assert.Equal(suite.T(), 0, len(subs)) } From 50a44231039b9419bd4ec02da826d3e696b86ad3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 31 Dec 2023 15:48:49 +0100 Subject: [PATCH 087/240] Fix race conditions --- cmd/evse/main.go | 4 +-- cmd/hems/main.go | 4 +-- service/hub.go | 64 +++++++++++++++------------------ service/mdns.go | 74 ++++++++++++++++++++++++++++++--------- service/mock_hub_test.go | 4 +-- service/mock_mdns_test.go | 2 +- service/service.go | 8 ++--- service/types.go | 48 ++++++++++++++++++++++--- 8 files changed, 141 insertions(+), 67 deletions(-) diff --git a/cmd/evse/main.go b/cmd/evse/main.go index e2f1a44a..6d8440c2 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -100,8 +100,8 @@ func (h *evse) VisibleRemoteServicesUpdated(service *service.EEBUSService, entri func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *evse) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) { - if ski == remoteSki && detail.State == service.ConnectionStateRemoteDeniedTrust { +func (h *evse) ServicePairingDetailUpdate(ski string, detail *service.ConnectionStateDetail) { + if ski == remoteSki && detail.State() == service.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") h.myService.RegisterRemoteSKI(ski, false) h.myService.CancelPairingWithSKI(ski) diff --git a/cmd/hems/main.go b/cmd/hems/main.go index b09b7b77..54f49bf2 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -100,8 +100,8 @@ func (h *hems) VisibleRemoteServicesUpdated(service *service.EEBUSService, entri func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *hems) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) { - if ski == remoteSki && detail.State == service.ConnectionStateRemoteDeniedTrust { +func (h *hems) ServicePairingDetailUpdate(ski string, detail *service.ConnectionStateDetail) { + if ski == remoteSki && detail.State() == service.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") h.myService.RegisterRemoteSKI(ski, false) h.myService.CancelPairingWithSKI(ski) diff --git a/service/hub.go b/service/hub.go index 832bbb1c..3ef0c91a 100644 --- a/service/hub.go +++ b/service/hub.go @@ -45,7 +45,7 @@ var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ // interface for reporting data from connectionsHub to the EEBUSService type ServiceProvider interface { // report a newly discovered remote EEBUS service - VisibleMDNSRecordsUpdated(entries []MdnsEntry) + VisibleMDNSRecordsUpdated(entries []*MdnsEntry) // report a connection to a SKI RemoteSKIConnected(ski string) @@ -58,7 +58,7 @@ type ServiceProvider interface { ServiceShipIDUpdate(ski string, shipID string) // provides the current handshake state for a given SKI - ServicePairingDetailUpdate(ski string, detail ConnectionStateDetail) + ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) // return if the user is still able to trust the connection AllowWaitingForTrust(ski string) bool @@ -87,7 +87,7 @@ type connectionsHub struct { mdns MdnsService // list of currently known/reported mDNS entries - knownMdnsEntries []MdnsEntry + knownMdnsEntries []*MdnsEntry // the SPINE local device spineLocalDevice *spine.DeviceLocalImpl @@ -104,7 +104,7 @@ func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineL connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), remoteServices: make(map[string]*ServiceDetails), - knownMdnsEntries: make([]MdnsEntry, 0), + knownMdnsEntries: make([]*MdnsEntry, 0), serviceProvider: serviceProvider, spineLocalDevice: spineLocalDevice, configuration: configuration, @@ -230,8 +230,6 @@ func (h *connectionsHub) AllowWaitingForTrust(ski string) bool { // Provides the current ship message exchange state for a given SKI and the corresponding error if state is error func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.ShipState) { - service := h.serviceForSKI(ski) - // overwrite service Paired value if state.State == ship.SmeHelloStateOk { h.RegisterRemoteSKI(ski, true) @@ -242,13 +240,12 @@ func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.S pairingState = ConnectionStateError } - pairingDetail := ConnectionStateDetail{ - State: pairingState, - Error: state.Error, - } + pairingDetail := NewConnectionStateDetail(pairingState, state.Error) + + service := h.serviceForSKI(ski) existingDetails := service.ConnectionStateDetail - if existingDetails.State != pairingState || existingDetails.Error != state.Error { + if existingDetails.State() != pairingState || existingDetails.Error() != state.Error { service.ConnectionStateDetail = pairingDetail h.serviceProvider.ServicePairingDetailUpdate(ski, pairingDetail) @@ -261,16 +258,13 @@ func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.S // // ErrNotPaired if the SKI is not in the (to be) paired list // ErrNoConnectionFound if no connection for the SKI was found -func (h *connectionsHub) PairingDetailForSki(ski string) ConnectionStateDetail { +func (h *connectionsHub) PairingDetailForSki(ski string) *ConnectionStateDetail { service := h.serviceForSKI(ski) if conn := h.connectionForSKI(ski); conn != nil { shipState, shipError := conn.ShipHandshakeState() state := h.mapShipMessageExchangeState(shipState, ski) - return ConnectionStateDetail{ - State: state, - Error: shipError, - } + return NewConnectionStateDetail(state, shipError) } return service.ConnectionStateDetail @@ -457,8 +451,8 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Check if the remote service is paired service := h.serviceForSKI(remoteService.SKI) - if service.ConnectionStateDetail.State == ConnectionStateQueued { - service.ConnectionStateDetail.State = ConnectionStateReceivedPairingRequest + if service.ConnectionStateDetail.State() == ConnectionStateQueued { + service.ConnectionStateDetail.SetState(ConnectionStateReceivedPairingRequest) h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) } @@ -601,7 +595,7 @@ func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { service, ok := h.remoteServices[ski] if !ok { service = NewServiceDetails(ski) - service.ConnectionStateDetail.State = ConnectionStateNone + service.ConnectionStateDetail.SetState(ConnectionStateNone) h.remoteServices[ski] = service } @@ -622,7 +616,7 @@ func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { h.removeConnectionAttemptCounter(ski) - service.ConnectionStateDetail.State = ConnectionStateNone + service.ConnectionStateDetail.SetState(ConnectionStateNone) h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) @@ -644,7 +638,7 @@ func (h *connectionsHub) InitiatePairingWithSKI(ski string) { // locally initiated service := h.serviceForSKI(ski) - service.ConnectionStateDetail.State = ConnectionStateQueued + service.ConnectionStateDetail.SetState(ConnectionStateQueued) h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) @@ -663,18 +657,18 @@ func (h *connectionsHub) CancelPairingWithSKI(ski string) { } service := h.serviceForSKI(ski) - service.ConnectionStateDetail.State = ConnectionStateNone + service.ConnectionStateDetail.SetState(ConnectionStateNone) service.Trusted = false h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) } // Process reported mDNS services -func (h *connectionsHub) ReportMdnsEntries(entries map[string]MdnsEntry) { +func (h *connectionsHub) ReportMdnsEntries(entries map[string]*MdnsEntry) { h.muxMdns.Lock() defer h.muxMdns.Unlock() - var mdnsEntries []MdnsEntry + var mdnsEntries []*MdnsEntry for ski, entry := range entries { mdnsEntries = append(mdnsEntries, entry) @@ -686,8 +680,8 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]MdnsEntry) { // Check if the remote service is paired or queued for connection service := h.serviceForSKI(ski) - pairingState := service.ConnectionStateDetail.State - if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != ConnectionStateQueued { + if !h.IsRemoteServiceForSKIPaired(ski) && + service.ConnectionStateDetail.State() != ConnectionStateQueued { continue } @@ -715,7 +709,7 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]MdnsEntry) { } // coordinate connection initiation attempts to a remove service -func (h *connectionsHub) coordinateConnectionInitations(ski string, entry MdnsEntry) { +func (h *connectionsHub) coordinateConnectionInitations(ski string, entry *MdnsEntry) { if h.isConnectionAttemptRunning(ski) { return } @@ -725,7 +719,7 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry MdnsEn counter, duration := h.getConnectionInitiationDelayTime(ski) service := h.serviceForSKI(ski) - if service.ConnectionStateDetail.State == ConnectionStateQueued { + if service.ConnectionStateDetail.State() == ConnectionStateQueued { go h.prepareConnectionInitation(ski, counter, entry) return } @@ -744,12 +738,9 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry MdnsEn // invoked by coordinateConnectionInitations either with a delay or directly // when initating a pairing process -func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, entry MdnsEntry) { +func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, entry *MdnsEntry) { h.setConnectionAttemptRunning(ski, false) - // check if the remoteService still exists - service := h.serviceForSKI(ski) - // check if the current counter is still the same, otherwise this counter is irrelevant currentCounter, exists := h.getCurrentConnectionAttemptCounter(ski) if !exists || currentCounter != counter { @@ -758,7 +749,7 @@ func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, ent // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing - pairingState := h.serviceForSKI(ski).ConnectionStateDetail.State + pairingState := h.serviceForSKI(ski).ConnectionStateDetail.State() if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != ConnectionStateQueued { return } @@ -769,6 +760,9 @@ func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, ent } // now initiate the connection + // check if the remoteService still exists + service := h.serviceForSKI(ski) + if success := h.initateConnection(service, entry); !success { h.checkRestartMdnsSearch() } @@ -776,14 +770,14 @@ func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, ent // attempt to establish a connection to a remote service // returns true if successful -func (h *connectionsHub) initateConnection(remoteService *ServiceDetails, entry MdnsEntry) bool { +func (h *connectionsHub) initateConnection(remoteService *ServiceDetails, entry *MdnsEntry) bool { var err error // try connecting via an IP address first for _, address := range entry.Addresses { // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing - pairingState := h.serviceForSKI(remoteService.SKI).ConnectionStateDetail.State + pairingState := h.serviceForSKI(remoteService.SKI).ConnectionStateDetail.State() if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != ConnectionStateQueued { return false } diff --git a/service/mdns.go b/service/mdns.go index c377f70d..7649b277 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -33,7 +33,7 @@ type MdnsEntry struct { // implemented by hubConnection, used by mdns type MdnsSearch interface { - ReportMdnsEntries(entries map[string]MdnsEntry) + ReportMdnsEntries(entries map[string]*MdnsEntry) } // implemented by mdns, used by hubConnection @@ -56,21 +56,22 @@ type mdnsManager struct { cancelChan chan bool // the currently available mDNS entries with the SKI as the key in the map - entries map[string]MdnsEntry + entries map[string]*MdnsEntry // the registered callback, only connectionsHub is using this searchDelegate MdnsSearch mdnsProvider mdns.MdnsProvider - mux sync.Mutex + mux sync.Mutex + entriesMux sync.Mutex } func newMDNS(ski string, configuration *Configuration) *mdnsManager { m := &mdnsManager{ ski: ski, configuration: configuration, - entries: make(map[string]MdnsEntry), + entries: make(map[string]*MdnsEntry), cancelChan: make(chan bool), } @@ -203,6 +204,49 @@ func (m *mdnsManager) setIsSearchingServices(enable bool) { m.isSearchingServices = enable } +func (m *mdnsManager) mdnsEntries() map[string]*MdnsEntry { + m.entriesMux.Lock() + defer m.entriesMux.Unlock() + + return m.entries +} + +func (m *mdnsManager) copyMdnsEntries() map[string]*MdnsEntry { + m.entriesMux.Lock() + defer m.entriesMux.Unlock() + + mdnsEntries := make(map[string]*MdnsEntry) + for k, v := range m.entries { + newEntry := &MdnsEntry{} + util.DeepCopy[*MdnsEntry](v, newEntry) + mdnsEntries[k] = newEntry + } + + return mdnsEntries +} + +func (m *mdnsManager) mdnsEntry(ski string) (*MdnsEntry, bool) { + m.entriesMux.Lock() + defer m.entriesMux.Unlock() + + entry, ok := m.entries[ski] + return entry, ok +} + +func (m *mdnsManager) setMdnsEntry(ski string, entry *MdnsEntry) { + m.entriesMux.Lock() + defer m.entriesMux.Unlock() + + m.entries[ski] = entry +} + +func (m *mdnsManager) removeMdnsEntry(ski string) { + m.entriesMux.Lock() + defer m.entriesMux.Unlock() + + delete(m.entries, ski) +} + // Register a callback to be invoked for found mDNS entries func (m *mdnsManager) RegisterMdnsSearch(cb MdnsSearch) { m.mux.Lock() @@ -218,12 +262,12 @@ func (m *mdnsManager) RegisterMdnsSearch(cb MdnsSearch) { } // do we already know some entries? - if len(m.entries) == 0 { + if len(m.mdnsEntries()) == 0 { return } // maybe entries are already found - mdnsEntries := m.entries + mdnsEntries := m.copyMdnsEntries() go m.searchDelegate.ReportMdnsEntries(mdnsEntries) } @@ -315,18 +359,17 @@ func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host st updated := true - _, exists := m.entries[ski] + entry, exists := m.mdnsEntry(ski) if remove && exists { // remove // there will be a remove for each address with avahi, but we'll delete it right away - delete(m.entries, ski) + m.removeMdnsEntry(ski) } else if exists { // update updated = false // avahi sends an item for each network address, merge them - entry := m.entries[ski] // we assume only network addresses are added for _, address := range addresses { @@ -346,10 +389,10 @@ func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host st } } - m.entries[ski] = entry + m.setMdnsEntry(ski, entry) } else if !exists && !remove { // new - newEntry := MdnsEntry{ + newEntry := &MdnsEntry{ Name: name, Ski: ski, Identifier: identifier, @@ -362,7 +405,7 @@ func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host st Port: port, Addresses: addresses, } - m.entries[ski] = newEntry + m.setMdnsEntry(ski, newEntry) logging.Log.Debug("ski:", ski, "name:", name, "brand:", brand, "model:", model, "typ:", deviceType, "identifier:", identifier, "register:", register, "host:", host, "port:", port, "addresses:", addresses) } else { @@ -370,10 +413,7 @@ func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host st } if m.searchDelegate != nil && updated { - mdnsEntries := make(map[string]MdnsEntry) - for k, v := range m.entries { - mdnsEntries[k] = v - } - go m.searchDelegate.ReportMdnsEntries(mdnsEntries) + entries := m.copyMdnsEntries() + go m.searchDelegate.ReportMdnsEntries(entries) } } diff --git a/service/mock_hub_test.go b/service/mock_hub_test.go index 25ccdb98..99f8b912 100644 --- a/service/mock_hub_test.go +++ b/service/mock_hub_test.go @@ -72,7 +72,7 @@ func (mr *MockServiceProviderMockRecorder) RemoteSKIDisconnected(arg0 interface{ } // ServicePairingDetailUpdate mocks base method. -func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 ConnectionStateDetail) { +func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 *ConnectionStateDetail) { m.ctrl.T.Helper() m.ctrl.Call(m, "ServicePairingDetailUpdate", arg0, arg1) } @@ -96,7 +96,7 @@ func (mr *MockServiceProviderMockRecorder) ServiceShipIDUpdate(arg0, arg1 interf } // VisibleMDNSRecordsUpdated mocks base method. -func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []MdnsEntry) { +func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []*MdnsEntry) { m.ctrl.T.Helper() m.ctrl.Call(m, "VisibleMDNSRecordsUpdated", arg0) } diff --git a/service/mock_mdns_test.go b/service/mock_mdns_test.go index 45d6efc3..2207663f 100644 --- a/service/mock_mdns_test.go +++ b/service/mock_mdns_test.go @@ -34,7 +34,7 @@ func (m *MockMdnsSearch) EXPECT() *MockMdnsSearchMockRecorder { } // ReportMdnsEntries mocks base method. -func (m *MockMdnsSearch) ReportMdnsEntries(arg0 map[string]MdnsEntry) { +func (m *MockMdnsSearch) ReportMdnsEntries(arg0 map[string]*MdnsEntry) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportMdnsEntries", arg0) } diff --git a/service/service.go b/service/service.go index cf164be0..15ea9359 100644 --- a/service/service.go +++ b/service/service.go @@ -38,7 +38,7 @@ type EEBUSServiceHandler interface { // Provides the current pairing state for the remote service // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process - ServicePairingDetailUpdate(ski string, detail ConnectionStateDetail) + ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) // return if the user is still able to trust the connection AllowWaitingForTrust(ski string) bool @@ -73,7 +73,7 @@ func NewEEBUSService(configuration *Configuration, serviceHandler EEBUSServiceHa var _ ServiceProvider = (*EEBUSService)(nil) -func (s *EEBUSService) VisibleMDNSRecordsUpdated(entries []MdnsEntry) { +func (s *EEBUSService) VisibleMDNSRecordsUpdated(entries []*MdnsEntry) { var remoteServices []RemoteService for _, entry := range entries { @@ -109,12 +109,12 @@ func (s *EEBUSService) ServiceShipIDUpdate(ski string, shipdID string) { // Provides the current pairing state for the remote service // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process -func (s *EEBUSService) ServicePairingDetailUpdate(ski string, detail ConnectionStateDetail) { +func (s *EEBUSService) ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) { s.serviceHandler.ServicePairingDetailUpdate(ski, detail) } // Get the current pairing details for a given SKI -func (s *EEBUSService) PairingDetailForSki(ski string) ConnectionStateDetail { +func (s *EEBUSService) PairingDetailForSki(ski string) *ConnectionStateDetail { return s.connectionsHub.PairingDetailForSki(ski) } diff --git a/service/types.go b/service/types.go index ee710041..aa537f54 100644 --- a/service/types.go +++ b/service/types.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "errors" "fmt" + "sync" "time" "github.com/enbility/eebus-go/spine/model" @@ -30,8 +31,45 @@ const ( // the connection state of a service and error if applicable type ConnectionStateDetail struct { - State ConnectionState - Error error + state ConnectionState + error error + + mux sync.Mutex +} + +func NewConnectionStateDetail(state ConnectionState, err error) *ConnectionStateDetail { + return &ConnectionStateDetail{ + state: state, + error: err, + } +} + +func (c *ConnectionStateDetail) State() ConnectionState { + c.mux.Lock() + defer c.mux.Unlock() + + return c.state +} + +func (c *ConnectionStateDetail) SetState(state ConnectionState) { + c.mux.Lock() + defer c.mux.Unlock() + + c.state = state +} + +func (c *ConnectionStateDetail) Error() error { + c.mux.Lock() + defer c.mux.Unlock() + + return c.error +} + +func (c *ConnectionStateDetail) SetError(err error) { + c.mux.Lock() + defer c.mux.Unlock() + + c.error = err } // generic service details about the local or any remote service @@ -63,13 +101,15 @@ type ServiceDetails struct { Trusted bool // the current connection state details - ConnectionStateDetail ConnectionStateDetail + ConnectionStateDetail *ConnectionStateDetail } // create a new ServiceDetails record with a SKI func NewServiceDetails(ski string) *ServiceDetails { + connState := NewConnectionStateDetail(ConnectionStateNone, nil) service := &ServiceDetails{ - SKI: util.NormalizeSKI(ski), // standardize the provided SKI strings + SKI: util.NormalizeSKI(ski), // standardize the provided SKI strings + ConnectionStateDetail: connState, } return service From 97166494e7e1d389b8b8892f7ab12b450240e18d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 31 Dec 2023 23:14:37 +0100 Subject: [PATCH 088/240] Also remove bindings for a removed entity --- spine/nodemanagement_defaileddiscovery.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spine/nodemanagement_defaileddiscovery.go b/spine/nodemanagement_defaileddiscovery.go index ce3f61e0..c9395945 100644 --- a/spine/nodemanagement_defaileddiscovery.go +++ b/spine/nodemanagement_defaileddiscovery.go @@ -193,12 +193,16 @@ func (r *NodeManagementImpl) processNotifyDetailedDiscoveryData(message *Message } Events.Publish(payload) - subscriptionMgr := r.Device().SubscriptionManager() // remove all subscriptions for this entity + subscriptionMgr := r.Device().SubscriptionManager() subscriptionMgr.RemoveSubscriptionsForEntity(removedEntity) // make sure Heartbeat Manager is up to date r.Device().HeartbeatManager().UpdateHeartbeatOnSubscriptions() + + // remove all bindings for this entity + bindingMgr := r.Device().BindingManager() + bindingMgr.RemoveBindingsForEntity(removedEntity) } } From 26177040298e34a15893bd368b62c5fee921a2be Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 1 Jan 2024 14:35:22 +0100 Subject: [PATCH 089/240] Reorganize tests a bit --- spine/helper_test.go | 165 ++++++++++++++++++ spine/mocks/Sender.go | 38 +++- ...go => nodemanagement_detaileddiscovery.go} | 0 .../nodemanagement_detaileddiscovery_test.go | 17 +- spine/send_test.go | 20 +-- .../nm_destinationListData_recv_read.json | 0 ...stinationListData_send_reply_expected.json | 0 .../nm_detaileddiscoverydata_recv_read.json | 0 ...m_detaileddiscoverydata_recv_read_ack.json | 0 ...aileddiscoverydata_send_read_expected.json | 0 ...ileddiscoverydata_send_reply_expected.json | 0 ...leddiscoverydata_send_result_expected.json | 0 .../nm_subscriptionRequestCall_recv_call.json | 0 ...ptionRequestCall_send_result_expected.json | 0 ...box_detaileddiscoverydata_recv_notify.json | 0 ...lbox_detaileddiscoverydata_recv_reply.json | 0 16 files changed, 212 insertions(+), 28 deletions(-) create mode 100644 spine/helper_test.go rename spine/{nodemanagement_defaileddiscovery.go => nodemanagement_detaileddiscovery.go} (100%) rename integration_tests/nodemanagement_test.go => spine/nodemanagement_detaileddiscovery_test.go (93%) rename {integration_tests => spine}/testdata/nm_destinationListData_recv_read.json (100%) rename {integration_tests => spine}/testdata/nm_destinationListData_send_reply_expected.json (100%) rename {integration_tests => spine}/testdata/nm_detaileddiscoverydata_recv_read.json (100%) rename {integration_tests => spine}/testdata/nm_detaileddiscoverydata_recv_read_ack.json (100%) rename {integration_tests => spine}/testdata/nm_detaileddiscoverydata_send_read_expected.json (100%) rename {integration_tests => spine}/testdata/nm_detaileddiscoverydata_send_reply_expected.json (100%) rename {integration_tests => spine}/testdata/nm_detaileddiscoverydata_send_result_expected.json (100%) rename {integration_tests => spine}/testdata/nm_subscriptionRequestCall_recv_call.json (100%) rename {integration_tests => spine}/testdata/nm_subscriptionRequestCall_send_result_expected.json (100%) rename {integration_tests => spine}/testdata/wallbox_detaileddiscoverydata_recv_notify.json (100%) rename {integration_tests => spine}/testdata/wallbox_detaileddiscoverydata_recv_reply.json (100%) diff --git a/spine/helper_test.go b/spine/helper_test.go new file mode 100644 index 00000000..8fe67a16 --- /dev/null +++ b/spine/helper_test.go @@ -0,0 +1,165 @@ +package spine + +import ( + "encoding/json" + "fmt" + "os" + "sync" + "testing" + + "github.com/enbility/eebus-go/spine/model" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/assert" +) + +const ( + wallbox_detaileddiscoverydata_recv_reply_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_reply.json" + wallbox_detaileddiscoverydata_recv_notify_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_notify.json" +) + +type WriteMessageHandler struct { + sentMessages [][]byte + + mux sync.Mutex +} + +var _ SpineDataConnection = (*WriteMessageHandler)(nil) + +func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { + t.mux.Lock() + defer t.mux.Unlock() + + t.sentMessages = append(t.sentMessages, message) +} + +func (t *WriteMessageHandler) LastMessage() []byte { + t.mux.Lock() + defer t.mux.Unlock() + + if len(t.sentMessages) == 0 { + return nil + } + + return t.sentMessages[len(t.sentMessages)-1] +} + +func (t *WriteMessageHandler) MessageWithReference(msgCounterReference *model.MsgCounterType) []byte { + t.mux.Lock() + defer t.mux.Unlock() + + var datagram model.Datagram + + for _, msg := range t.sentMessages { + if err := json.Unmarshal(msg, &datagram); err != nil { + return nil + } + if datagram.Datagram.Header.MsgCounterReference == nil { + continue + } + if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { + continue + } + if datagram.Datagram.Payload.Cmd[0].ResultData != nil { + continue + } + + return msg + } + + return nil +} + +func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.MsgCounterType) []byte { + t.mux.Lock() + defer t.mux.Unlock() + + var datagram model.Datagram + + for _, msg := range t.sentMessages { + if err := json.Unmarshal(msg, &datagram); err != nil { + return nil + } + if datagram.Datagram.Header.MsgCounterReference == nil { + continue + } + if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { + continue + } + if datagram.Datagram.Payload.Cmd[0].ResultData == nil { + continue + } + + return msg + } + + return nil +} + +func loadFileData(t *testing.T, fileName string) []byte { + fileData, err := os.ReadFile(fileName) + if err != nil { + t.Fatal(err) + } + + return fileData +} + +func checkSentData(t *testing.T, sendBytes []byte, msgSendFilePrefix string) { + msgSendExpectedBytes, err := os.ReadFile(msgSendFilePrefix + "_expected.json") + if err != nil { + t.Fatal(err) + } + + msgSendActualFileName := msgSendFilePrefix + "_actual.json" + equal := jsonDatagramEqual(t, msgSendExpectedBytes, sendBytes) + if !equal { + saveJsonToFile(t, sendBytes, msgSendActualFileName) + } + assert.Truef(t, equal, "Assert equal failed! Check '%s' ", msgSendActualFileName) +} + +func jsonDatagramEqual(t *testing.T, expectedJson, actualJson []byte) bool { + var actualDatagram model.Datagram + if err := json.Unmarshal(actualJson, &actualDatagram); err != nil { + t.Fatal(err) + } + var expectedDatagram model.Datagram + if err := json.Unmarshal(expectedJson, &expectedDatagram); err != nil { + t.Fatal(err) + } + + less := func(a, b model.FunctionPropertyType) bool { return string(*a.Function) < string(*b.Function) } + return cmp.Equal(expectedDatagram, actualDatagram, cmpopts.SortSlices(less)) +} + +func saveJsonToFile(t *testing.T, data json.RawMessage, fileName string) { + jsonIndent, err := json.MarshalIndent(data, "", " ") + if err != nil { + t.Fatal(err) + } + err = os.WriteFile(fileName, jsonIndent, os.ModePerm) + if err != nil { + t.Fatal(err) + } +} + +func waitForAck(t *testing.T, msgCounterReference *model.MsgCounterType, writeHandler *WriteMessageHandler) { + var datagram model.Datagram + + msg := writeHandler.ResultWithReference(msgCounterReference) + if msg == nil { + t.Fatal("acknowledge message was not sent!!") + } + + if err := json.Unmarshal(msg, &datagram); err != nil { + t.Fatal(err) + } + + cmd := datagram.Datagram.Payload.Cmd[0] + if cmd.ResultData != nil { + if cmd.ResultData.ErrorNumber != nil && uint(*cmd.ResultData.ErrorNumber) != uint(model.ErrorNumberTypeNoError) { + t.Fatal(fmt.Errorf("error '%d' result data received", uint(*cmd.ResultData.ErrorNumber))) + } + } +} diff --git a/spine/mocks/Sender.go b/spine/mocks/Sender.go index af686605..058564f9 100644 --- a/spine/mocks/Sender.go +++ b/spine/mocks/Sender.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.36.1. DO NOT EDIT. +// Code generated by mockery v2.39.1. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type Sender struct { func (_m *Sender) Bind(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) { ret := _m.Called(senderAddress, destinationAddress, serverFeatureType) + if len(ret) == 0 { + panic("no return value specified for Bind") + } + var r0 *model.MsgCounterType var r1 error if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) (*model.MsgCounterType, error)); ok { @@ -44,6 +48,10 @@ func (_m *Sender) Bind(senderAddress *model.FeatureAddressType, destinationAddre func (_m *Sender) DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) { ret := _m.Called(msgCounter) + if len(ret) == 0 { + panic("no return value specified for DatagramForMsgCounter") + } + var r0 model.DatagramType var r1 error if rf, ok := ret.Get(0).(func(model.MsgCounterType) (model.DatagramType, error)); ok { @@ -68,6 +76,10 @@ func (_m *Sender) DatagramForMsgCounter(msgCounter model.MsgCounterType) (model. func (_m *Sender) Notify(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) { ret := _m.Called(senderAddress, destinationAddress, cmd) + if len(ret) == 0 { + panic("no return value specified for Notify") + } + var r0 *model.MsgCounterType var r1 error if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) (*model.MsgCounterType, error)); ok { @@ -94,6 +106,10 @@ func (_m *Sender) Notify(senderAddress *model.FeatureAddressType, destinationAdd func (_m *Sender) Reply(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, cmd model.CmdType) error { ret := _m.Called(requestHeader, senderAddress, cmd) + if len(ret) == 0 { + panic("no return value specified for Reply") + } + var r0 error if rf, ok := ret.Get(0).(func(*model.HeaderType, *model.FeatureAddressType, model.CmdType) error); ok { r0 = rf(requestHeader, senderAddress, cmd) @@ -108,6 +124,10 @@ func (_m *Sender) Reply(requestHeader *model.HeaderType, senderAddress *model.Fe func (_m *Sender) Request(cmdClassifier model.CmdClassifierType, senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, ackRequest bool, cmd []model.CmdType) (*model.MsgCounterType, error) { ret := _m.Called(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) + if len(ret) == 0 { + panic("no return value specified for Request") + } + var r0 *model.MsgCounterType var r1 error if rf, ok := ret.Get(0).(func(model.CmdClassifierType, *model.FeatureAddressType, *model.FeatureAddressType, bool, []model.CmdType) (*model.MsgCounterType, error)); ok { @@ -134,6 +154,10 @@ func (_m *Sender) Request(cmdClassifier model.CmdClassifierType, senderAddress * func (_m *Sender) ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *spine.ErrorType) error { ret := _m.Called(requestHeader, senderAddress, err) + if len(ret) == 0 { + panic("no return value specified for ResultError") + } + var r0 error if rf, ok := ret.Get(0).(func(*model.HeaderType, *model.FeatureAddressType, *spine.ErrorType) error); ok { r0 = rf(requestHeader, senderAddress, err) @@ -148,6 +172,10 @@ func (_m *Sender) ResultError(requestHeader *model.HeaderType, senderAddress *mo func (_m *Sender) ResultSuccess(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType) error { ret := _m.Called(requestHeader, senderAddress) + if len(ret) == 0 { + panic("no return value specified for ResultSuccess") + } + var r0 error if rf, ok := ret.Get(0).(func(*model.HeaderType, *model.FeatureAddressType) error); ok { r0 = rf(requestHeader, senderAddress) @@ -162,6 +190,10 @@ func (_m *Sender) ResultSuccess(requestHeader *model.HeaderType, senderAddress * func (_m *Sender) Subscribe(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) { ret := _m.Called(senderAddress, destinationAddress, serverFeatureType) + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + var r0 *model.MsgCounterType var r1 error if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) (*model.MsgCounterType, error)); ok { @@ -188,6 +220,10 @@ func (_m *Sender) Subscribe(senderAddress *model.FeatureAddressType, destination func (_m *Sender) Write(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) { ret := _m.Called(senderAddress, destinationAddress, cmd) + if len(ret) == 0 { + panic("no return value specified for Write") + } + var r0 *model.MsgCounterType var r1 error if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) (*model.MsgCounterType, error)); ok { diff --git a/spine/nodemanagement_defaileddiscovery.go b/spine/nodemanagement_detaileddiscovery.go similarity index 100% rename from spine/nodemanagement_defaileddiscovery.go rename to spine/nodemanagement_detaileddiscovery.go diff --git a/integration_tests/nodemanagement_test.go b/spine/nodemanagement_detaileddiscovery_test.go similarity index 93% rename from integration_tests/nodemanagement_test.go rename to spine/nodemanagement_detaileddiscovery_test.go index 01733fa2..70f670ea 100644 --- a/integration_tests/nodemanagement_test.go +++ b/spine/nodemanagement_detaileddiscovery_test.go @@ -1,10 +1,9 @@ -package integrationtests +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -28,11 +27,11 @@ func TestNodeManagementSuite(t *testing.T) { type NodeManagementSuite struct { suite.Suite - sut *spine.DeviceLocalImpl + sut *DeviceLocalImpl remoteSki string - readHandler spine.SpineDataProcessing + readHandler SpineDataProcessing writeHandler *WriteMessageHandler } @@ -40,7 +39,7 @@ func (s *NodeManagementSuite) SetupSuite() { } func (s *NodeManagementSuite) BeforeTest(suiteName, testName string) { - s.sut = spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", + s.sut = NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) s.remoteSki = "TestRemoteSki" @@ -81,7 +80,7 @@ func (s *NodeManagementSuite) TestDetailedDiscovery_RecvReply() { rEntities := remoteDevice.Entities() assert.Equal(s.T(), 2, len(rEntities)) - di := rEntities[spine.DeviceInformationEntityId] + di := rEntities[DeviceInformationEntityId] assert.NotNil(s.T(), di) assert.Equal(s.T(), model.EntityTypeTypeDeviceInformation, di.EntityType()) @@ -89,7 +88,7 @@ func (s *NodeManagementSuite) TestDetailedDiscovery_RecvReply() { assert.Equal(s.T(), 2, len(diFeatures)) nm := diFeatures[0] - assert.Equal(s.T(), spine.NodeManagementFeatureId, uint(*nm.Address().Feature)) + assert.Equal(s.T(), NodeManagementFeatureId, uint(*nm.Address().Feature)) assert.Equal(s.T(), model.FeatureTypeTypeNodeManagement, nm.Type()) assert.Equal(s.T(), model.RoleTypeSpecial, nm.Role()) assert.Equal(s.T(), 8, len(nm.Operations())) @@ -143,7 +142,7 @@ func (s *NodeManagementSuite) TestDetailedDiscovery_RecvNotifyAdded() { rEntities := remoteDevice.Entities() if assert.Equal(s.T(), 3, len(rEntities)) { { - di := rEntities[spine.DeviceInformationEntityId] + di := rEntities[DeviceInformationEntityId] assert.NotNil(s.T(), di) assert.Equal(s.T(), model.EntityTypeTypeDeviceInformation, di.EntityType()) assert.Equal(s.T(), 2, len(di.Features())) @@ -185,7 +184,7 @@ func (s *NodeManagementSuite) TestSubscriptionRequestCall_BeforeDetailedDiscover remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) subscriptionsForDevice := s.sut.SubscriptionManager().Subscriptions(remoteDevice) assert.Equal(s.T(), 1, len(subscriptionsForDevice)) - subscriptionsOnFeature := s.sut.SubscriptionManager().SubscriptionsOnFeature(*spine.NodeManagementAddress(s.sut.Address())) + subscriptionsOnFeature := s.sut.SubscriptionManager().SubscriptionsOnFeature(*NodeManagementAddress(s.sut.Address())) assert.Equal(s.T(), 1, len(subscriptionsOnFeature)) } diff --git a/spine/send_test.go b/spine/send_test.go index 025eb4e8..6310b5ad 100644 --- a/spine/send_test.go +++ b/spine/send_test.go @@ -2,7 +2,6 @@ package spine_test import ( "encoding/json" - "sync" "testing" "github.com/enbility/eebus-go/spine" @@ -11,23 +10,8 @@ import ( "github.com/stretchr/testify/assert" ) -type WriteMessageHandler struct { - sentMessage []byte - - mux sync.Mutex -} - -var _ spine.SpineDataConnection = (*WriteMessageHandler)(nil) - -func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { - t.mux.Lock() - defer t.mux.Unlock() - - t.sentMessage = message -} - func TestSender_Notify_MsgCounter(t *testing.T) { - temp := &WriteMessageHandler{} + temp := &spine.WriteMessageHandler{} sut := spine.NewSender(temp) senderAddress := featureAddressType(1, spine.NewEntityAddressType("Sender", []uint{1})) @@ -44,7 +28,7 @@ func TestSender_Notify_MsgCounter(t *testing.T) { assert.NoError(t, err) expectedMsgCounter := 2 //because Notify was called twice - sentBytes := temp.sentMessage + sentBytes := temp.LastMessage() var sentDatagram model.Datagram assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) diff --git a/integration_tests/testdata/nm_destinationListData_recv_read.json b/spine/testdata/nm_destinationListData_recv_read.json similarity index 100% rename from integration_tests/testdata/nm_destinationListData_recv_read.json rename to spine/testdata/nm_destinationListData_recv_read.json diff --git a/integration_tests/testdata/nm_destinationListData_send_reply_expected.json b/spine/testdata/nm_destinationListData_send_reply_expected.json similarity index 100% rename from integration_tests/testdata/nm_destinationListData_send_reply_expected.json rename to spine/testdata/nm_destinationListData_send_reply_expected.json diff --git a/integration_tests/testdata/nm_detaileddiscoverydata_recv_read.json b/spine/testdata/nm_detaileddiscoverydata_recv_read.json similarity index 100% rename from integration_tests/testdata/nm_detaileddiscoverydata_recv_read.json rename to spine/testdata/nm_detaileddiscoverydata_recv_read.json diff --git a/integration_tests/testdata/nm_detaileddiscoverydata_recv_read_ack.json b/spine/testdata/nm_detaileddiscoverydata_recv_read_ack.json similarity index 100% rename from integration_tests/testdata/nm_detaileddiscoverydata_recv_read_ack.json rename to spine/testdata/nm_detaileddiscoverydata_recv_read_ack.json diff --git a/integration_tests/testdata/nm_detaileddiscoverydata_send_read_expected.json b/spine/testdata/nm_detaileddiscoverydata_send_read_expected.json similarity index 100% rename from integration_tests/testdata/nm_detaileddiscoverydata_send_read_expected.json rename to spine/testdata/nm_detaileddiscoverydata_send_read_expected.json diff --git a/integration_tests/testdata/nm_detaileddiscoverydata_send_reply_expected.json b/spine/testdata/nm_detaileddiscoverydata_send_reply_expected.json similarity index 100% rename from integration_tests/testdata/nm_detaileddiscoverydata_send_reply_expected.json rename to spine/testdata/nm_detaileddiscoverydata_send_reply_expected.json diff --git a/integration_tests/testdata/nm_detaileddiscoverydata_send_result_expected.json b/spine/testdata/nm_detaileddiscoverydata_send_result_expected.json similarity index 100% rename from integration_tests/testdata/nm_detaileddiscoverydata_send_result_expected.json rename to spine/testdata/nm_detaileddiscoverydata_send_result_expected.json diff --git a/integration_tests/testdata/nm_subscriptionRequestCall_recv_call.json b/spine/testdata/nm_subscriptionRequestCall_recv_call.json similarity index 100% rename from integration_tests/testdata/nm_subscriptionRequestCall_recv_call.json rename to spine/testdata/nm_subscriptionRequestCall_recv_call.json diff --git a/integration_tests/testdata/nm_subscriptionRequestCall_send_result_expected.json b/spine/testdata/nm_subscriptionRequestCall_send_result_expected.json similarity index 100% rename from integration_tests/testdata/nm_subscriptionRequestCall_send_result_expected.json rename to spine/testdata/nm_subscriptionRequestCall_send_result_expected.json diff --git a/integration_tests/testdata/wallbox_detaileddiscoverydata_recv_notify.json b/spine/testdata/wallbox_detaileddiscoverydata_recv_notify.json similarity index 100% rename from integration_tests/testdata/wallbox_detaileddiscoverydata_recv_notify.json rename to spine/testdata/wallbox_detaileddiscoverydata_recv_notify.json diff --git a/integration_tests/testdata/wallbox_detaileddiscoverydata_recv_reply.json b/spine/testdata/wallbox_detaileddiscoverydata_recv_reply.json similarity index 100% rename from integration_tests/testdata/wallbox_detaileddiscoverydata_recv_reply.json rename to spine/testdata/wallbox_detaileddiscoverydata_recv_reply.json From ae8323308b61e10e61e4bcddf16d4a58c0bc2954 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 1 Jan 2024 14:38:36 +0100 Subject: [PATCH 090/240] Fixes for previous commit --- integration_tests/helper_test.go | 46 ++------------------------------ 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/integration_tests/helper_test.go b/integration_tests/helper_test.go index 6d09d057..21e57c38 100644 --- a/integration_tests/helper_test.go +++ b/integration_tests/helper_test.go @@ -10,14 +10,11 @@ import ( "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/stretchr/testify/assert" ) const ( - wallbox_detaileddiscoverydata_recv_reply_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_reply.json" - wallbox_detaileddiscoverydata_recv_notify_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_notify.json" + wallbox_detaileddiscoverydata_recv_reply_file_path = "../spine/testdata/wallbox_detaileddiscoverydata_recv_reply.json" + wallbox_detaileddiscoverydata_recv_notify_file_path = "../spine/testdata/wallbox_detaileddiscoverydata_recv_notify.json" ) type WriteMessageHandler struct { @@ -133,45 +130,6 @@ func loadFileData(t *testing.T, fileName string) []byte { return fileData } -func checkSentData(t *testing.T, sendBytes []byte, msgSendFilePrefix string) { - msgSendExpectedBytes, err := os.ReadFile(msgSendFilePrefix + "_expected.json") - if err != nil { - t.Fatal(err) - } - - msgSendActualFileName := msgSendFilePrefix + "_actual.json" - equal := jsonDatagramEqual(t, msgSendExpectedBytes, sendBytes) - if !equal { - saveJsonToFile(t, sendBytes, msgSendActualFileName) - } - assert.Truef(t, equal, "Assert equal failed! Check '%s' ", msgSendActualFileName) -} - -func jsonDatagramEqual(t *testing.T, expectedJson, actualJson []byte) bool { - var actualDatagram model.Datagram - if err := json.Unmarshal(actualJson, &actualDatagram); err != nil { - t.Fatal(err) - } - var expectedDatagram model.Datagram - if err := json.Unmarshal(expectedJson, &expectedDatagram); err != nil { - t.Fatal(err) - } - - less := func(a, b model.FunctionPropertyType) bool { return string(*a.Function) < string(*b.Function) } - return cmp.Equal(expectedDatagram, actualDatagram, cmpopts.SortSlices(less)) -} - -func saveJsonToFile(t *testing.T, data json.RawMessage, fileName string) { - jsonIndent, err := json.MarshalIndent(data, "", " ") - if err != nil { - t.Fatal(err) - } - err = os.WriteFile(fileName, jsonIndent, os.ModePerm) - if err != nil { - t.Fatal(err) - } -} - func waitForAck(t *testing.T, msgCounterReference *model.MsgCounterType, writeHandler *WriteMessageHandler) { var datagram model.Datagram From 9bbf1f626d89c297c1f2ded3ad9c40b1a8f4bb80 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Jan 2024 20:05:24 +0100 Subject: [PATCH 091/240] Add more SHIP Connection tests --- ship/connection_test.go | 133 +++++++++++++++++++++++++++++ spine/mocks/SpineDataConnection.go | 29 +++++++ spine/mocks/SpineDataProcessing.go | 57 +++++++++++++ spine/types.go | 4 + 4 files changed, 223 insertions(+) create mode 100644 spine/mocks/SpineDataConnection.go create mode 100644 spine/mocks/SpineDataProcessing.go diff --git a/ship/connection_test.go b/ship/connection_test.go index ddbf6d02..97b68b89 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -7,9 +7,11 @@ import ( "github.com/enbility/eebus-go/ship/model" "github.com/enbility/eebus-go/spine" + spineMocks "github.com/enbility/eebus-go/spine/mocks" spineModel "github.com/enbility/eebus-go/spine/model" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -25,6 +27,8 @@ type ConnectionSuite struct { shipDataProvider *MockShipServiceDataProvider shipDataConn *MockShipDataConnection + spineDataProcessing *spineMocks.SpineDataProcessing + sentMessage []byte } @@ -39,15 +43,144 @@ func (s *ConnectionSuite) BeforeTest(suiteName, testName string) { ctrl := gomock.NewController(s.T()) s.shipDataProvider = NewMockShipServiceDataProvider(ctrl) + s.shipDataProvider.EXPECT().HandleShipHandshakeStateUpdate(gomock.Any(), gomock.Any()).AnyTimes() + s.shipDataProvider.EXPECT().HandleConnectionClosed(gomock.Any(), gomock.Any()).AnyTimes() s.shipDataConn = NewMockShipDataConnection(ctrl) s.shipDataConn.EXPECT().InitDataProcessing(gomock.Any()).AnyTimes() s.shipDataConn.EXPECT().WriteMessageToDataConnection(gomock.Any()).DoAndReturn(func(message []byte) error { s.sentMessage = message; return nil }).AnyTimes() s.shipDataConn.EXPECT().IsDataConnectionClosed().DoAndReturn(func() (bool, error) { return false, nil }).AnyTimes() + s.shipDataConn.EXPECT().CloseDataConnection(gomock.Any(), gomock.Any()).AnyTimes() + + s.spineDataProcessing = spineMocks.NewSpineDataProcessing(s.T()) s.sut = NewConnectionHandler(s.shipDataProvider, s.shipDataConn, localDevice, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID") } +func (s *ConnectionSuite) TestRun() { + s.sut.Run() + assert.Equal(s.T(), CmiStateServerWait, s.sut.smeState) +} + +func (s *ConnectionSuite) TestShipHandshakeState() { + state, err := s.sut.ShipHandshakeState() + assert.Nil(s.T(), err) + assert.Equal(s.T(), CmiStateInitStart, state) +} + +func (s *ConnectionSuite) TestApprovePendingHandshake() { + s.sut.smeState = CmiStateInitStart + s.sut.ApprovePendingHandshake() + assert.Equal(s.T(), CmiStateInitStart, s.sut.smeState) + + s.sut.smeState = SmeHelloStatePendingListen + s.sut.ApprovePendingHandshake() + assert.Equal(s.T(), SmeProtHStateServerListenProposal, s.sut.smeState) +} + +func (s *ConnectionSuite) TestAbortPendingHandshake() { + s.sut.smeState = CmiStateInitStart + s.sut.AbortPendingHandshake() + assert.Equal(s.T(), CmiStateInitStart, s.sut.smeState) + + s.sut.smeState = SmeHelloStatePendingListen + s.sut.AbortPendingHandshake() + assert.Equal(s.T(), SmeHelloStateAbortDone, s.sut.smeState) +} + +func (s *ConnectionSuite) TestCloseConnection_StateComplete() { + s.sut.smeState = SmeStateComplete + s.sut.CloseConnection(true, 450, "User Close") + state, err := s.sut.ShipHandshakeState() + assert.Nil(s.T(), err) + assert.Equal(s.T(), SmeStateComplete, state) +} + +func (s *ConnectionSuite) TestCloseConnection_StateComplete_2() { + s.sut.smeState = SmeStateError + s.sut.CloseConnection(false, 0, "User Close") + state, err := s.sut.ShipHandshakeState() + assert.Nil(s.T(), err) + assert.Equal(s.T(), SmeStateError, state) +} + +func (s *ConnectionSuite) TestCloseConnection_StateComplete_3() { + s.sut.smeState = SmeStateError + s.sut.CloseConnection(false, 450, "User Close") + state, err := s.sut.ShipHandshakeState() + assert.Nil(s.T(), err) + assert.Equal(s.T(), SmeStateError, state) +} + +func (s *ConnectionSuite) TestShipModelFromMessage() { + msg := []byte{} + data, err := s.sut.shipModelFromMessage(msg) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + modelData := model.ShipData{} + jsonData, err := json.Marshal(modelData) + assert.Nil(s.T(), err) + + msg = []byte{0} + msg = append(msg, jsonData...) + data, err = s.sut.shipModelFromMessage(msg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) +} + +func (s *ConnectionSuite) TestHandleIncomingShipMessage() { + modelData := model.ShipData{} + jsonData, err := json.Marshal(modelData) + assert.Nil(s.T(), err) + + msg := []byte{0} + msg = append(msg, jsonData...) + + s.sut.HandleIncomingShipMessage(msg) + + spineData := spineModel.Datagram{} + jsonData, err = json.Marshal(spineData) + assert.Nil(s.T(), err) + + rawBytes := []byte{} + rawBytes = append(rawBytes, jsonData...) + modelData = model.ShipData{ + Data: model.DataType{ + Payload: rawBytes, + }, + } + jsonData, err = json.Marshal(modelData) + assert.Nil(s.T(), err) + + msg = []byte{0} + msg = append(msg, jsonData...) + + s.sut.HandleIncomingShipMessage(msg) + + s.spineDataProcessing.On("HandleIncomingSpineMesssage", mock.Anything).Return(nil, nil) + s.sut.spineDataProcessing = s.spineDataProcessing + + s.sut.HandleIncomingShipMessage(msg) +} + +func (s *ConnectionSuite) TestReportConnectionError() { + s.sut.ReportConnectionError(nil) + assert.Equal(s.T(), SmeStateError, s.sut.smeState) + + s.sut.smeState = SmeHelloStateReadyListen + s.sut.ReportConnectionError(nil) + assert.Equal(s.T(), SmeHelloStateRejected, s.sut.smeState) + + s.sut.smeState = SmeHelloStateRemoteAbortDone + s.sut.ReportConnectionError(nil) + assert.Equal(s.T(), SmeHelloStateRemoteAbortDone, s.sut.smeState) + + s.sut.smeState = SmeHelloStateAbort + s.sut.ReportConnectionError(nil) + assert.Equal(s.T(), SmeHelloStateAbort, s.sut.smeState) +} + func (s *ConnectionSuite) TestSendShipModel() { err := s.sut.sendShipModel(model.MsgTypeInit, nil) assert.NotNil(s.T(), err) diff --git a/spine/mocks/SpineDataConnection.go b/spine/mocks/SpineDataConnection.go new file mode 100644 index 00000000..5be34a68 --- /dev/null +++ b/spine/mocks/SpineDataConnection.go @@ -0,0 +1,29 @@ +// Code generated by mockery v2.39.1. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// SpineDataConnection is an autogenerated mock type for the SpineDataConnection type +type SpineDataConnection struct { + mock.Mock +} + +// WriteSpineMessage provides a mock function with given fields: message +func (_m *SpineDataConnection) WriteSpineMessage(message []byte) { + _m.Called(message) +} + +// NewSpineDataConnection creates a new instance of SpineDataConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSpineDataConnection(t interface { + mock.TestingT + Cleanup(func()) +}) *SpineDataConnection { + mock := &SpineDataConnection{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/spine/mocks/SpineDataProcessing.go b/spine/mocks/SpineDataProcessing.go new file mode 100644 index 00000000..11a48825 --- /dev/null +++ b/spine/mocks/SpineDataProcessing.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.39.1. DO NOT EDIT. + +package mocks + +import ( + model "github.com/enbility/eebus-go/spine/model" + mock "github.com/stretchr/testify/mock" +) + +// SpineDataProcessing is an autogenerated mock type for the SpineDataProcessing type +type SpineDataProcessing struct { + mock.Mock +} + +// HandleIncomingSpineMesssage provides a mock function with given fields: message +func (_m *SpineDataProcessing) HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) { + ret := _m.Called(message) + + if len(ret) == 0 { + panic("no return value specified for HandleIncomingSpineMesssage") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (*model.MsgCounterType, error)); ok { + return rf(message) + } + if rf, ok := ret.Get(0).(func([]byte) *model.MsgCounterType); ok { + r0 = rf(message) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(message) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewSpineDataProcessing creates a new instance of SpineDataProcessing. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSpineDataProcessing(t interface { + mock.TestingT + Cleanup(func()) +}) *SpineDataProcessing { + mock := &SpineDataProcessing{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/spine/types.go b/spine/types.go index 36c4543c..2d75e077 100644 --- a/spine/types.go +++ b/spine/types.go @@ -2,6 +2,8 @@ package spine import "github.com/enbility/eebus-go/spine/model" +//go:generate mockery --name=SpineDataProcessing + // Used to pass an incoming SPINE message from a SHIP connection to the proper DeviceRemoteImpl // // Implemented by DeviceRemoteImpl, used by ShipConnection @@ -9,6 +11,8 @@ type SpineDataProcessing interface { HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) } +//go:generate mockery --name=SpineDataConnection + // Used to pass an outgoing SPINE message from a DeviceLocalImpl to the SHIP connection // // Implemented by ShipConnection, used by DeviceLocalImpl From d6b301a4bacc7c5bab4b9c5e1f89a66094318c72 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Jan 2024 21:32:49 +0100 Subject: [PATCH 092/240] Add websocket tests & fix data race possibility --- ship/websocket.go | 4 ++ ship/websocket_test.go | 151 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 ship/websocket_test.go diff --git a/ship/websocket.go b/ship/websocket.go index 2bb1d092..4b24dc49 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -101,7 +101,9 @@ func (w *websocketConnection) writeShipPump() { return } + w.muxConWrite.Lock() _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) + w.muxConWrite.Unlock() if !ok { logging.Log.Debug(w.remoteSki, "Ship write channel closed") // The write channel has been closed @@ -136,7 +138,9 @@ func (w *websocketConnection) writeShipPump() { return } + w.muxConWrite.Lock() _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) + w.muxConWrite.Unlock() if err := w.writeMessage(websocket.PingMessage, nil); err != nil { logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) w.setConnClosedError(err) diff --git a/ship/websocket_test.go b/ship/websocket_test.go new file mode 100644 index 00000000..c9f57edc --- /dev/null +++ b/ship/websocket_test.go @@ -0,0 +1,151 @@ +package ship + +import ( + "log" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/gorilla/websocket" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestWebsocketSuite(t *testing.T) { + suite.Run(t, new(WebsocketSuite)) +} + +type WebsocketSuite struct { + suite.Suite + + sut *websocketConnection + + testServer *httptest.Server + testWsConn *websocket.Conn + + shipDataProcessing *MockShipDataProcessing +} + +func (s *WebsocketSuite) SetupSuite() {} +func (s *WebsocketSuite) TearDownTest() {} + +func (s *WebsocketSuite) BeforeTest(suiteName, testName string) { + ctrl := gomock.NewController(s.T()) + + s.shipDataProcessing = NewMockShipDataProcessing(ctrl) + s.shipDataProcessing.EXPECT().ReportConnectionError(gomock.Any()).AnyTimes() + s.shipDataProcessing.EXPECT().HandleIncomingShipMessage(gomock.Any()).AnyTimes() + + ts := &testServer{} + s.testServer, s.testWsConn = newWSServer(s.T(), ts) + + s.sut = NewWebsocketConnection(s.testWsConn, "remoteSki") + s.sut.InitDataProcessing(s.shipDataProcessing) +} + +func (s *WebsocketSuite) AfterTest(suiteName, testName string) { + s.testWsConn.Close() + s.testServer.Close() +} + +func (s *WebsocketSuite) TestConnection() { + isClosed := s.sut.isConnClosed() + assert.Equal(s.T(), false, isClosed) + + msg := []byte{0, 0} + err := s.sut.WriteMessageToDataConnection(msg) + assert.Nil(s.T(), err) + + // make sure we have enough time to read and write + time.Sleep(time.Millisecond * 500) + + msg = []byte{1} + msg = append(msg, []byte("message")...) + err = s.sut.WriteMessageToDataConnection(msg) + assert.Nil(s.T(), err) + + // make sure we have enough time to read and write + time.Sleep(time.Millisecond * 500) + + isConnClosed, err := s.sut.IsDataConnectionClosed() + assert.Equal(s.T(), false, isConnClosed) + assert.Nil(s.T(), err) + + s.sut.CloseDataConnection(450, "User Close") + + isConnClosed, err = s.sut.IsDataConnectionClosed() + assert.Equal(s.T(), true, isConnClosed) + assert.NotNil(s.T(), err) + + err = s.sut.WriteMessageToDataConnection(msg) + assert.NotNil(s.T(), err) +} + +func (s *WebsocketSuite) TestConnectionClose() { + s.sut.close() + + isClosed, err := s.sut.IsDataConnectionClosed() + assert.Equal(s.T(), true, isClosed) + assert.NotNil(s.T(), err) +} + +func (s *WebsocketSuite) TestPingPeriod() { + isClosed, err := s.sut.IsDataConnectionClosed() + assert.Equal(s.T(), false, isClosed) + assert.Nil(s.T(), err) + + // make sure we have enough time to read and write + time.Sleep(time.Second * 51) + + isClosed, err = s.sut.IsDataConnectionClosed() + assert.Equal(s.T(), false, isClosed) + assert.Nil(s.T(), err) +} + +var upgrader = websocket.Upgrader{} + +func newWSServer(t *testing.T, h http.Handler) (*httptest.Server, *websocket.Conn) { + t.Helper() + + s := httptest.NewServer(h) + wsURL := strings.Replace(s.URL, "http://", "ws://", -1) + wsURL = strings.Replace(wsURL, "https://", "wss://", -1) + + ws, _, err := websocket.DefaultDialer.Dial(wsURL, nil) + if err != nil { + t.Fatal(err) + } + + return s, ws +} + +type testServer struct { +} + +func (s *testServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + upgrader.CheckOrigin = func(r *http.Request) bool { return true } + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("upgrade:", err) + return + } + defer ws.Close() + + for { + _, msg, err := ws.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { + log.Printf("error: %v", err) + } + return + } + + err = ws.WriteMessage(websocket.BinaryMessage, msg) + if err != nil { + continue + } + } +} From 941b7240736ce3cdb373f589053812a6ee06a752 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Jan 2024 21:37:19 +0100 Subject: [PATCH 093/240] Skip Websocket ping test on CI --- ship/helper_test.go | 12 ++++++++++++ ship/hs_helper_test.go | 8 -------- ship/websocket_test.go | 2 ++ 3 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 ship/helper_test.go diff --git a/ship/helper_test.go b/ship/helper_test.go new file mode 100644 index 00000000..5980fefd --- /dev/null +++ b/ship/helper_test.go @@ -0,0 +1,12 @@ +package ship + +import ( + "os" + "testing" +) + +func skipCI(t *testing.T) { + if os.Getenv("ACTION_ENVIRONMENT") == "CI" { + t.Skip("Skipping testing in CI environment") + } +} diff --git a/ship/hs_helper_test.go b/ship/hs_helper_test.go index 510f9180..10f2a8ef 100644 --- a/ship/hs_helper_test.go +++ b/ship/hs_helper_test.go @@ -1,9 +1,7 @@ package ship import ( - "os" "sync" - "testing" "time" "github.com/enbility/eebus-go/spine" @@ -64,9 +62,3 @@ func initTest(role shipRole) (*ShipConnection, *dataHandlerTest) { func shutdownTest(conhandler *ShipConnection) { conhandler.stopHandshakeTimer() } - -func skipCI(t *testing.T) { - if os.Getenv("ACTION_ENVIRONMENT") == "CI" { - t.Skip("Skipping testing in CI environment") - } -} diff --git a/ship/websocket_test.go b/ship/websocket_test.go index c9f57edc..06431ac0 100644 --- a/ship/websocket_test.go +++ b/ship/websocket_test.go @@ -93,6 +93,8 @@ func (s *WebsocketSuite) TestConnectionClose() { } func (s *WebsocketSuite) TestPingPeriod() { + skipCI(s.T()) + isClosed, err := s.sut.IsDataConnectionClosed() assert.Equal(s.T(), false, isClosed) assert.Nil(s.T(), err) From e205f75fa85a522bf74e369a8abc89bbe2ecb6e7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Jan 2024 22:13:37 +0100 Subject: [PATCH 094/240] Improve CI testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don’t ignore the tests completely, but don’t test via time sleep triggers but calling the corresponding methods directly --- ship/handshake.go | 3 +-- ship/helper_test.go | 7 ++----- ship/hs_hello.go | 5 +++++ ship/hs_hello_test.go | 20 ++++++++++++++------ ship/websocket.go | 30 +++++++++++++++++------------- ship/websocket_test.go | 11 +++++++---- 6 files changed, 46 insertions(+), 30 deletions(-) diff --git a/ship/handshake.go b/ship/handshake.go index 3df62c64..b68522ab 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -135,8 +135,7 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { case SmeHelloStateReadyListen: if timeout { - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.handshakeHello_ReadyTimeout() return } diff --git a/ship/helper_test.go b/ship/helper_test.go index 5980fefd..ba860071 100644 --- a/ship/helper_test.go +++ b/ship/helper_test.go @@ -2,11 +2,8 @@ package ship import ( "os" - "testing" ) -func skipCI(t *testing.T) { - if os.Getenv("ACTION_ENVIRONMENT") == "CI" { - t.Skip("Skipping testing in CI environment") - } +func isRunningOnCI() bool { + return os.Getenv("ACTION_ENVIRONMENT") == "CI" } diff --git a/ship/hs_hello.go b/ship/hs_hello.go index eb9cb57b..be7a30d8 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -76,6 +76,11 @@ func (c *ShipConnection) handshakeHello_ReadyListen(message []byte) { c.handleState(false, nil) } +func (c *ShipConnection) handshakeHello_ReadyTimeout() { + c.setState(SmeHelloStateAbort, nil) + c.handleState(false, nil) +} + // SME_HELLO_ABORT func (c *ShipConnection) handshakeHello_Abort() { c.stopHandshakeTimer() diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go index 4c5cff75..4b22ec07 100644 --- a/ship/hs_hello_test.go +++ b/ship/hs_hello_test.go @@ -70,14 +70,18 @@ func (s *HelloSuite) Test_ReadyListen_Ok() { } func (s *HelloSuite) Test_ReadyListen_Timeout() { - skipCI(s.T()) - sut, data := initTest(s.role) sut.setState(SmeHelloStateReadyInit, nil) // inits the timer sut.setState(SmeHelloStateReadyListen, nil) - time.Sleep(tHelloInit + time.Second) + if !isRunningOnCI() { + // test if the function is triggered correctly via the timer + time.Sleep(tHelloInit + time.Second) + } else { + // speed up the test by running the method directly + sut.handshakeHello_ReadyTimeout() + } assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) @@ -157,14 +161,18 @@ func (s *HelloSuite) Test_PendingListen() { } func (s *HelloSuite) Test_PendingListen_Timeout() { - skipCI(s.T()) - sut, data := initTest(s.role) sut.setState(SmeHelloStatePendingInit, nil) // inits the timer sut.setState(SmeHelloStatePendingListen, nil) - time.Sleep(tHelloInit + time.Second) + if !isRunningOnCI() { + // test if the function is triggered correctly via the timer + time.Sleep(tHelloInit + time.Second) + } else { + // speed up the test by running the method directly + sut.handshakeHello_PendingTimeout() + } assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) assert.NotNil(s.T(), data.lastMessage()) diff --git a/ship/websocket.go b/ship/websocket.go index 4b24dc49..f8838f06 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -134,23 +134,27 @@ func (w *websocketConnection) writeShipPump() { logging.Log.Trace("Send:", w.remoteSki, text) case <-ticker.C: - if w.isConnClosed() { - return - } - - w.muxConWrite.Lock() - _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) - w.muxConWrite.Unlock() - if err := w.writeMessage(websocket.PingMessage, nil); err != nil { - logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) - w.setConnClosedError(err) - w.dataProcessing.ReportConnectionError(err) - return - } + w.handlePing() } } } +func (w *websocketConnection) handlePing() { + if w.isConnClosed() { + return + } + + w.muxConWrite.Lock() + _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) + w.muxConWrite.Unlock() + if err := w.writeMessage(websocket.PingMessage, nil); err != nil { + logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) + w.setConnClosedError(err) + w.dataProcessing.ReportConnectionError(err) + return + } +} + // readShipPump checks for messages from the websocket connection func (w *websocketConnection) readShipPump() { _ = w.conn.SetReadDeadline(time.Now().Add(pongWait)) diff --git a/ship/websocket_test.go b/ship/websocket_test.go index 06431ac0..53fd3f9c 100644 --- a/ship/websocket_test.go +++ b/ship/websocket_test.go @@ -93,14 +93,17 @@ func (s *WebsocketSuite) TestConnectionClose() { } func (s *WebsocketSuite) TestPingPeriod() { - skipCI(s.T()) - isClosed, err := s.sut.IsDataConnectionClosed() assert.Equal(s.T(), false, isClosed) assert.Nil(s.T(), err) - // make sure we have enough time to read and write - time.Sleep(time.Second * 51) + if !isRunningOnCI() { + // test if the function is triggered correctly via the timer + time.Sleep(time.Second * 51) + } else { + // speed up the test by running the method directly + s.sut.handlePing() + } isClosed, err = s.sut.IsDataConnectionClosed() assert.Equal(s.T(), false, isClosed) From 24974134dc0d34aca9b396673815f5407ebce926 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Jan 2024 22:33:02 +0100 Subject: [PATCH 095/240] Improving HelloSuite Pending Listen tests --- ship/handshake.go | 14 +------------- ship/hs_hello.go | 14 +++++++++++++- ship/hs_hello_test.go | 19 ++++++++++++++++++- ship/hs_helper_test.go | 8 ++++++-- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/ship/handshake.go b/ship/handshake.go index b68522ab..1dc2c553 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -145,19 +145,7 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { c.handshakeHello_PendingInit() case SmeHelloStatePendingListen: - if timeout { - // The device needs to be in a state for the user to allow trusting the device - // e.g. either the web UI or by other means - if !c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { - c.handshakeHello_PendingTimeout() - return - } - - c.handshakeHello_PendingProlongationRequest() - return - } - - c.handshakeHello_PendingListen(message) + c.handshakeHello_PendingListen(timeout, message) case SmeHelloStateOk: c.handshakeProtocol_Init() diff --git a/ship/hs_hello.go b/ship/hs_hello.go index be7a30d8..3c7c15a0 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -110,7 +110,19 @@ func (c *ShipConnection) handshakeHello_PendingInit() { } // SME_HELLO_PENDING_LISTEN -func (c *ShipConnection) handshakeHello_PendingListen(message []byte) { +func (c *ShipConnection) handshakeHello_PendingListen(timeout bool, message []byte) { + if timeout { + // The device needs to be in a state for the user to allow trusting the device + // e.g. either the web UI or by other means + if !c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { + c.handshakeHello_PendingTimeout() + } else { + c.handshakeHello_PendingProlongationRequest() + } + + return + } + var helloReturnMsg model.ConnectionHello if err := c.processShipJsonMessage(message, &helloReturnMsg); err != nil { c.setState(SmeHelloStateAbort, nil) diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go index 4b22ec07..0babb1ee 100644 --- a/ship/hs_hello_test.go +++ b/ship/hs_hello_test.go @@ -171,7 +171,7 @@ func (s *HelloSuite) Test_PendingListen_Timeout() { time.Sleep(tHelloInit + time.Second) } else { // speed up the test by running the method directly - sut.handshakeHello_PendingTimeout() + sut.handshakeHello_PendingListen(true, nil) } assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) @@ -180,6 +180,23 @@ func (s *HelloSuite) Test_PendingListen_Timeout() { shutdownTest(sut) } +func (s *HelloSuite) Test_PendingListen_Timeout_Prolongation() { + sut, data := initTest(s.role) + + data.allowWaitingForTrust = true + + sut.setState(SmeHelloStatePendingInit, nil) // inits the timer + sut.setState(SmeHelloStatePendingListen, nil) + + // speed up the test by running the method directly, the timer is already checked + sut.handshakeHello_PendingListen(true, nil) + + assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) + assert.NotNil(s.T(), data.lastMessage()) + + shutdownTest(sut) +} + func (s *HelloSuite) Test_PendingListen_ReadyAbort() { sut, data := initTest(s.role) diff --git a/ship/hs_helper_test.go b/ship/hs_helper_test.go index 10f2a8ef..ca86f32d 100644 --- a/ship/hs_helper_test.go +++ b/ship/hs_helper_test.go @@ -13,6 +13,8 @@ type dataHandlerTest struct { mux sync.Mutex + allowWaitingForTrust bool + handleConnectionClosedInvoked bool } @@ -45,8 +47,10 @@ func (s *dataHandlerTest) IsRemoteServiceForSKIPaired(string) bool { return true func (s *dataHandlerTest) HandleConnectionClosed(*ShipConnection, bool) { s.handleConnectionClosedInvoked = true } -func (s *dataHandlerTest) ReportServiceShipID(string, string) {} -func (s *dataHandlerTest) AllowWaitingForTrust(string) bool { return false } +func (s *dataHandlerTest) ReportServiceShipID(string, string) {} +func (s *dataHandlerTest) AllowWaitingForTrust(string) bool { + return s.allowWaitingForTrust +} func (s *dataHandlerTest) HandleShipHandshakeStateUpdate(string, ShipState) {} func initTest(role shipRole) (*ShipConnection, *dataHandlerTest) { From a079db6226a298d6601377ebda343aee0c8fb303 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Jan 2024 22:51:06 +0100 Subject: [PATCH 096/240] Add SHIP Hello Pending Prolongation test --- ship/handshake.go | 7 +------ ship/hs_hello.go | 7 ++++++- ship/hs_hello_test.go | 28 +++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/ship/handshake.go b/ship/handshake.go index 1dc2c553..f552aba3 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -134,12 +134,7 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { c.handshakeHello_Init() case SmeHelloStateReadyListen: - if timeout { - c.handshakeHello_ReadyTimeout() - return - } - - c.handshakeHello_ReadyListen(message) + c.handshakeHello_ReadyListen(timeout, message) case SmeHelloStatePendingInit: c.handshakeHello_PendingInit() diff --git a/ship/hs_hello.go b/ship/hs_hello.go index 3c7c15a0..e460d35a 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -22,7 +22,12 @@ func (c *ShipConnection) handshakeHello_Init() { } // SME_HELLO_STATE_READY_LISTEN -func (c *ShipConnection) handshakeHello_ReadyListen(message []byte) { +func (c *ShipConnection) handshakeHello_ReadyListen(timeout bool, message []byte) { + if timeout { + c.handshakeHello_ReadyTimeout() + return + } + var helloReturnMsg model.ConnectionHello if err := c.processShipJsonMessage(message, &helloReturnMsg); err != nil { c.setState(SmeHelloStateAbort, nil) diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go index 0babb1ee..db82e657 100644 --- a/ship/hs_hello_test.go +++ b/ship/hs_hello_test.go @@ -80,7 +80,7 @@ func (s *HelloSuite) Test_ReadyListen_Timeout() { time.Sleep(tHelloInit + time.Second) } else { // speed up the test by running the method directly - sut.handshakeHello_ReadyTimeout() + sut.handshakeHello_ReadyListen(true, nil) } assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) @@ -112,6 +112,32 @@ func (s *HelloSuite) Test_ReadyListen_Ignore() { shutdownTest(sut) } +func (s *HelloSuite) Test_ReadyListen_Prolongation() { + sut, data := initTest(s.role) + + sut.setState(SmeHelloStateReadyInit, nil) // inits the timer + sut.setState(SmeHelloStateReadyListen, nil) + + data.allowWaitingForTrust = true + + helloMsg := model.ConnectionHello{ + ConnectionHello: model.ConnectionHelloType{ + Phase: model.ConnectionHelloPhaseTypePending, + ProlongationRequest: util.Ptr(true), + }, + } + + msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), msg) + + sut.handleState(false, msg) + + assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) + + shutdownTest(sut) +} + func (s *HelloSuite) Test_ReadyListen_Abort() { sut, data := initTest(s.role) From 5de4005b4e114906db525a44fce2fcbdd58b8791 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Jan 2024 23:17:15 +0100 Subject: [PATCH 097/240] Improve SHIP Hello Protocol and tests --- ship/hs_prot.go | 24 ++++++++------- ship/hs_prot_client_test.go | 60 +++++++++++++++++++++++++++++++++++++ ship/hs_prot_server_test.go | 49 ++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/ship/hs_prot.go b/ship/hs_prot.go index bcacf6f7..b1b58568 100644 --- a/ship/hs_prot.go +++ b/ship/hs_prot.go @@ -112,38 +112,38 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateClientListenChoice(messa msgHandshake := messageProtocolHandshake.MessageProtocolHandshake + abort := false if msgHandshake.HandshakeType != model.ProtocolHandshakeTypeTypeSelect { logging.Log.Debug("invalid protocol handshake response") - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) - return + abort = true } if msgHandshake.Version.Major != 1 { logging.Log.Debug("unsupported protocol major version") - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) - return + abort = true } if msgHandshake.Version.Minor != 0 { logging.Log.Debug("unsupported protocol minor version") - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) - return + abort = true } if msgHandshake.Formats.Format == nil || len(msgHandshake.Formats.Format) == 0 { logging.Log.Debug("format is missing") - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) - return + abort = true } if len(msgHandshake.Formats.Format) != 1 { logging.Log.Debug("unsupported format response") - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) - return + abort = true } - if msgHandshake.Formats.Format[0] != model.MessageProtocolFormatTypeUTF8 { + if msgHandshake.Formats.Format != nil && msgHandshake.Formats.Format[0] != model.MessageProtocolFormatTypeUTF8 { logging.Log.Debug("unsupported format") + abort = true + } + + if abort { c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) return } @@ -171,5 +171,7 @@ func (c *ShipConnection) abortProtocolHandshake(err model.MessageProtocolHandsha _ = c.sendShipModel(model.MsgTypeControl, msg) + c.setState(SmeStateError, errors.New("handshake error")) + c.CloseConnection(false, 0, "") } diff --git a/ship/hs_prot_client_test.go b/ship/hs_prot_client_test.go index 826d37c9..dd8d074c 100644 --- a/ship/hs_prot_client_test.go +++ b/ship/hs_prot_client_test.go @@ -65,3 +65,63 @@ func (s *ProClientSuite) Test_ListenChoice() { shutdownTest(sut) } + +func (s *ProClientSuite) Test_ListenChoice_Failures() { + sut, data := initTest(s.role) + + sut.setState(SmeProtHStateClientListenChoice, nil) + + protMsg := model.MessageProtocolHandshake{ + MessageProtocolHandshake: model.MessageProtocolHandshakeType{ + HandshakeType: model.ProtocolHandshakeTypeTypeAnnounceMax, + Version: model.Version{Major: 0, Minor: 1}, + }, + } + + msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), msg) + + sut.handleState(false, msg) + + sut.setState(SmeProtHStateClientListenChoice, nil) + + protMsg = model.MessageProtocolHandshake{ + MessageProtocolHandshake: model.MessageProtocolHandshakeType{ + HandshakeType: model.ProtocolHandshakeTypeTypeAnnounceMax, + Version: model.Version{Major: 0, Minor: 1}, + Formats: model.MessageProtocolFormatsType{ + Format: []model.MessageProtocolFormatType{model.MessageProtocolFormatTypeUTF16}, + }, + }, + } + + msg, err = sut.shipMessage(model.MsgTypeControl, protMsg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), msg) + + sut.handleState(false, msg) + + assert.Equal(s.T(), false, sut.handshakeTimerRunning) + + assert.Equal(s.T(), SmeStateError, sut.getState()) + assert.NotNil(s.T(), data.lastMessage()) + + shutdownTest(sut) +} + +func (s *ProClientSuite) Test_Abort() { + sut, data := initTest(s.role) + + sut.setState(SmeProtHStateClientListenChoice, nil) + + sut.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeTimeout) + + assert.Equal(s.T(), SmeStateError, sut.getState()) + assert.NotNil(s.T(), data.lastMessage()) + + timer := sut.getHandshakeTimerRunnging() + assert.Equal(s.T(), false, timer) + + shutdownTest(sut) +} diff --git a/ship/hs_prot_server_test.go b/ship/hs_prot_server_test.go index 78fca7eb..a42fdb0a 100644 --- a/ship/hs_prot_server_test.go +++ b/ship/hs_prot_server_test.go @@ -66,6 +66,30 @@ func (s *ProServerSuite) Test_ListenProposal() { shutdownTest(sut) } +func (s *ProServerSuite) Test_ListenProposal_Failure() { + sut, _ := initTest(s.role) + + sut.setState(SmeProtHStateServerListenProposal, nil) + + protMsg := model.MessageProtocolHandshake{ + MessageProtocolHandshake: model.MessageProtocolHandshakeType{ + HandshakeType: model.ProtocolHandshakeTypeTypeSelect, + }, + } + + msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), msg) + + sut.handleState(false, msg) + + assert.Equal(s.T(), false, sut.handshakeTimerRunning) + + assert.Equal(s.T(), SmeStateError, sut.getState()) + + shutdownTest(sut) +} + func (s *ProServerSuite) Test_ListenConfirm() { sut, data := initTest(s.role) @@ -95,3 +119,28 @@ func (s *ProServerSuite) Test_ListenConfirm() { shutdownTest(sut) } + +func (s *ProServerSuite) Test_ListenConfirm_Failures() { + sut, data := initTest(s.role) + + sut.setState(SmeProtHStateServerListenConfirm, nil) + + protMsg := model.MessageProtocolHandshake{ + MessageProtocolHandshake: model.MessageProtocolHandshakeType{ + HandshakeType: model.ProtocolHandshakeTypeTypeAnnounceMax, + }, + } + + msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), msg) + + sut.handleState(false, msg) + + assert.Equal(s.T(), false, sut.handshakeTimerRunning) + + assert.Equal(s.T(), SmeStateError, sut.getState()) + assert.NotNil(s.T(), data.lastMessage()) + + shutdownTest(sut) +} From 194d3c84b4762653a4869df8808461fb52cf8dfc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Jan 2024 09:28:43 +0100 Subject: [PATCH 098/240] Further improve ship tests --- ship/connection.go | 9 ++---- ship/handshake.go | 12 +++++--- ship/hs_access_test.go | 63 ++++++++++++++++++++++++++++++++++++++++++ ship/hs_hello.go | 45 ++++++++++-------------------- ship/hs_init.go | 6 ++-- ship/hs_pin.go | 3 +- ship/hs_prot.go | 6 ++-- ship/websocket.go | 16 ++++++----- ship/websocket_test.go | 27 ++++++++++++++++++ 9 files changed, 130 insertions(+), 57 deletions(-) diff --git a/ship/connection.go b/ship/connection.go index 25deb6b4..583aecd7 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -109,12 +109,10 @@ func (c *ShipConnection) ApprovePendingHandshake() { // HELLO_OK c.stopHandshakeTimer() - c.setState(SmeHelloStateReadyInit, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateReadyInit) // TODO: check if we need to do some validations before moving on to the next state - c.setState(SmeHelloStateOk, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateOk) } // invoked when pairing for a pending request is denied @@ -129,8 +127,7 @@ func (c *ShipConnection) AbortPendingHandshake() { // TODO: Move this into hs_hello.go and add tests c.stopHandshakeTimer() - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) } // report removing a connection diff --git a/ship/handshake.go b/ship/handshake.go index f552aba3..fe119b45 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -167,12 +167,10 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { c.handshakeProtocol_smeProtHStateClientListenChoice(message) case SmeProtHStateClientOk: - c.setState(SmePinStateCheckInit, nil) - c.handleState(false, nil) + c.setAndHandleState(SmePinStateCheckInit) case SmeProtHStateServerOk: - c.setState(SmePinStateCheckInit, nil) - c.handleState(false, nil) + c.setAndHandleState(SmePinStateCheckInit) // smePinState @@ -192,6 +190,12 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { } } +// set a state and trigger handling it +func (c *ShipConnection) setAndHandleState(state ShipMessageExchangeState) { + c.setState(state, nil) + c.handleState(false, nil) +} + // SHIP handshake is approved, now set the new state and the SPINE read handler func (c *ShipConnection) approveHandshake() { // Report to SPINE local device about this remote device connection diff --git a/ship/hs_access_test.go b/ship/hs_access_test.go index 605d83b8..e8b4be3b 100644 --- a/ship/hs_access_test.go +++ b/ship/hs_access_test.go @@ -51,6 +51,24 @@ func (s *AccessSuite) Test_Request() { shutdownTest(sut) } +func (s *AccessSuite) Test_Request_Invalid() { + sut, _ := initTest(ShipRoleClient) + + sut.setState(SmeAccessMethodsRequest, nil) + + accessMsg := model.MessageProtocolHandshake{} + msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), msg) + + sut.handleState(false, msg) + + assert.Equal(s.T(), false, sut.handshakeTimerRunning) + assert.Equal(s.T(), SmeStateError, sut.getState()) + + shutdownTest(sut) +} + func (s *AccessSuite) Test_Methods_Ok() { sut, data := initTest(ShipRoleClient) @@ -74,6 +92,27 @@ func (s *AccessSuite) Test_Methods_Ok() { shutdownTest(sut) } +func (s *AccessSuite) Test_Methods_NoID() { + sut, data := initTest(ShipRoleClient) + + sut.setState(SmeAccessMethodsRequest, nil) + + accessMsg := model.AccessMethods{ + AccessMethods: model.AccessMethodsType{}, + } + msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), msg) + + sut.handleState(false, msg) + + assert.Equal(s.T(), false, sut.handshakeTimerRunning) + assert.Equal(s.T(), SmeStateError, sut.getState()) + assert.Nil(s.T(), data.lastMessage()) + + shutdownTest(sut) +} + func (s *AccessSuite) Test_Methods_WrongShipID() { sut, data := initTest(ShipRoleClient) @@ -96,3 +135,27 @@ func (s *AccessSuite) Test_Methods_WrongShipID() { shutdownTest(sut) } + +func (s *AccessSuite) Test_Methods_NoShipID() { + sut, _ := initTest(ShipRoleClient) + + sut.remoteShipID = "" + + sut.setState(SmeAccessMethodsRequest, nil) + + accessMsg := model.AccessMethods{ + AccessMethods: model.AccessMethodsType{ + Id: util.Ptr(""), + }, + } + msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), msg) + + sut.handleState(false, msg) + + assert.Equal(s.T(), false, sut.handshakeTimerRunning) + assert.Equal(s.T(), SmeStateComplete, sut.getState()) + + shutdownTest(sut) +} diff --git a/ship/hs_hello.go b/ship/hs_hello.go index e460d35a..0fab7a50 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -13,8 +13,7 @@ import ( // SME_HELLO_STATE_READY_INIT func (c *ShipConnection) handshakeHello_Init() { if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypeReady, tHelloInit, false); err != nil { - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) return } @@ -30,8 +29,7 @@ func (c *ShipConnection) handshakeHello_ReadyListen(timeout bool, message []byte var helloReturnMsg model.ConnectionHello if err := c.processShipJsonMessage(message, &helloReturnMsg); err != nil { - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) return } @@ -65,16 +63,14 @@ func (c *ShipConnection) handshakeHello_ReadyListen(timeout bool, message []byte // TODO: what to do if this is false? case model.ConnectionHelloPhaseTypeAborted: - c.setState(SmeHelloStateRemoteAbortDone, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateRemoteAbortDone) return default: // don't accept any other responses logging.Log.Errorf("Unexpected connection hello phase: %s", hello.Phase) - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) return } @@ -82,8 +78,7 @@ func (c *ShipConnection) handshakeHello_ReadyListen(timeout bool, message []byte } func (c *ShipConnection) handshakeHello_ReadyTimeout() { - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) } // SME_HELLO_ABORT @@ -95,8 +90,7 @@ func (c *ShipConnection) handshakeHello_Abort() { return } - c.setState(SmeHelloStateAbortDone, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbortDone) } // SME_HELLO_PENDING_INIT @@ -109,8 +103,7 @@ func (c *ShipConnection) handshakeHello_PendingInit() { c.setState(SmeHelloStatePendingListen, nil) if !c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) } } @@ -130,8 +123,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(timeout bool, message []by var helloReturnMsg model.ConnectionHello if err := c.processShipJsonMessage(message, &helloReturnMsg); err != nil { - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) return } @@ -140,8 +132,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(timeout bool, message []by switch hello.Phase { case model.ConnectionHelloPhaseTypeReady: if hello.Waiting == nil { - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) return } @@ -163,8 +154,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(timeout bool, message []by if newDuration < tHelloProlongMin { // I interpret 13.4.4.1.3 Page 64 Line 1550-1553 as this resulting in a timeout state // TODO: verify this - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) } case model.ConnectionHelloPhaseTypePending: @@ -188,8 +178,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(timeout bool, message []by if newDuration < tHelloProlongMin { // I interpret 13.4.4.1.3 Page 64 Line 1557-1560 as this resulting in a timeout state // TODO: verify this - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) } return @@ -204,19 +193,16 @@ func (c *ShipConnection) handshakeHello_PendingListen(timeout bool, message []by return } - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) case model.ConnectionHelloPhaseTypeAborted: - c.setState(SmeHelloStateRemoteAbortDone, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateRemoteAbortDone) return default: // don't accept any other responses logging.Log.Errorf("Unexpected connection hello phase: %s", hello.Phase) - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) return } @@ -235,8 +221,7 @@ func (c *ShipConnection) handshakeHello_PendingProlongationRequest() { func (c *ShipConnection) handshakeHello_PendingTimeout() { if c.getHandshakeTimerType() != timeoutTimerTypeSendProlongationRequest { - c.setState(SmeHelloStateAbort, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloStateAbort) return } diff --git a/ship/hs_init.go b/ship/hs_init.go index 5f190d0e..c55141ec 100644 --- a/ship/hs_init.go +++ b/ship/hs_init.go @@ -39,8 +39,7 @@ func (c *ShipConnection) handshakeInit_cmiStateServerWait(message []byte) { return } - c.setState(SmeHelloState, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloState) } // CMI_STATE_CLIENT_WAIT @@ -51,8 +50,7 @@ func (c *ShipConnection) handshakeInit_cmiStateClientWait(message []byte) { return } - c.setState(SmeHelloState, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeHelloState) } // CMI_STATE_SERVER_EVALUATE diff --git a/ship/hs_pin.go b/ship/hs_pin.go index bbded434..90b6f462 100644 --- a/ship/hs_pin.go +++ b/ship/hs_pin.go @@ -37,8 +37,7 @@ func (c *ShipConnection) handshakePin_smePinStateCheckListen(message []byte) { switch connectionPinState.ConnectionPinState.PinState { case model.PinStateTypeNone: - c.setState(SmePinStateCheckOk, nil) - c.handleState(false, nil) + c.setAndHandleState(SmePinStateCheckOk) case model.PinStateTypeRequired: c.endHandshakeWithError(errors.New("Got pin state: required (unsupported)")) case model.PinStateTypeOptional: diff --git a/ship/hs_prot.go b/ship/hs_prot.go index b1b58568..fe467c29 100644 --- a/ship/hs_prot.go +++ b/ship/hs_prot.go @@ -82,8 +82,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateServerListenConfirm(mess c.stopHandshakeTimer() - c.setState(SmeProtHStateServerOk, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeProtHStateServerOk) } func (c *ShipConnection) handshakeProtocol_smeProtHStateClientInit() { @@ -158,8 +157,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateClientListenChoice(messa return } - c.setState(SmeProtHStateClientOk, nil) - c.handleState(false, nil) + c.setAndHandleState(SmeProtHStateClientOk) } func (c *ShipConnection) abortProtocolHandshake(err model.MessageProtocolHandshakeErrorErrorType) { diff --git a/ship/websocket.go b/ship/websocket.go index f8838f06..e534eab1 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -105,7 +105,7 @@ func (w *websocketConnection) writeShipPump() { _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) w.muxConWrite.Unlock() if !ok { - logging.Log.Debug(w.remoteSki, "Ship write channel closed") + logging.Log.Debug(w.remoteSki, "ship write channel closed") // The write channel has been closed _ = w.writeMessage(websocket.CloseMessage, []byte{}) return @@ -117,9 +117,7 @@ func (w *websocketConnection) writeShipPump() { return } - logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) - w.setConnClosedError(err) - w.dataProcessing.ReportConnectionError(err) + w.closeWithError(err, "error writing to websocket: ") return } @@ -148,13 +146,17 @@ func (w *websocketConnection) handlePing() { _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) w.muxConWrite.Unlock() if err := w.writeMessage(websocket.PingMessage, nil); err != nil { - logging.Log.Debug(w.remoteSki, "error writing to websocket: ", err) - w.setConnClosedError(err) - w.dataProcessing.ReportConnectionError(err) + w.closeWithError(err, "error writing to websocket: ") return } } +func (w *websocketConnection) closeWithError(err error, reason string) { + logging.Log.Debug(w.remoteSki, reason, err) + w.setConnClosedError(err) + w.dataProcessing.ReportConnectionError(err) +} + // readShipPump checks for messages from the websocket connection func (w *websocketConnection) readShipPump() { _ = w.conn.SetReadDeadline(time.Now().Add(pongWait)) diff --git a/ship/websocket_test.go b/ship/websocket_test.go index 53fd3f9c..8a0fe5f3 100644 --- a/ship/websocket_test.go +++ b/ship/websocket_test.go @@ -1,6 +1,7 @@ package ship import ( + "errors" "log" "net/http" "net/http/httptest" @@ -84,6 +85,19 @@ func (s *WebsocketSuite) TestConnection() { assert.NotNil(s.T(), err) } +func (s *WebsocketSuite) TestConnectionInvalid() { + msg := []byte{100} + err := s.sut.WriteMessageToDataConnection(msg) + assert.Nil(s.T(), err) + + // make sure we have enough time to read and write + time.Sleep(time.Millisecond * 500) + + isConnClosed, err := s.sut.IsDataConnectionClosed() + assert.Equal(s.T(), true, isConnClosed) + assert.NotNil(s.T(), err) +} + func (s *WebsocketSuite) TestConnectionClose() { s.sut.close() @@ -110,6 +124,19 @@ func (s *WebsocketSuite) TestPingPeriod() { assert.Nil(s.T(), err) } +func (s *WebsocketSuite) TestCloseWithError() { + isClosed, err := s.sut.IsDataConnectionClosed() + assert.Equal(s.T(), false, isClosed) + assert.Nil(s.T(), err) + + err = errors.New("test error") + s.sut.closeWithError(err, "test error") + + isClosed, err = s.sut.IsDataConnectionClosed() + assert.Equal(s.T(), true, isClosed) + assert.NotNil(s.T(), err) +} + var upgrader = websocket.Upgrader{} func newWSServer(t *testing.T, h http.Handler) (*httptest.Server, *websocket.Conn) { From 352dd2b65b792e1378b61ddaeb5cfb1edbeb74d9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Jan 2024 10:46:43 +0100 Subject: [PATCH 099/240] Add more service hub tests --- service/hub_test.go | 185 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 7 deletions(-) diff --git a/service/hub_test.go b/service/hub_test.go index 9d842641..17ec46dd 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -1,10 +1,12 @@ package service import ( + "errors" "testing" "time" "github.com/enbility/eebus-go/ship" + "github.com/enbility/eebus-go/spine/model" gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -49,11 +51,14 @@ func (s *HubSuite) SetupSuite() { s.serviceProvider.EXPECT().RemoteSKIConnected(gomock.Any()).AnyTimes() s.serviceProvider.EXPECT().ServiceShipIDUpdate(gomock.Any(), gomock.Any()).AnyTimes() s.serviceProvider.EXPECT().ServicePairingDetailUpdate(gomock.Any(), gomock.Any()).AnyTimes() + s.serviceProvider.EXPECT().RemoteSKIDisconnected(gomock.Any()).AnyTimes() + s.serviceProvider.EXPECT().AllowWaitingForTrust(gomock.Any()).AnyTimes() s.mdnsService = NewMockMdnsService(ctrl) s.mdnsService.EXPECT().SetupMdnsService().AnyTimes() s.mdnsService.EXPECT().AnnounceMdnsEntry().AnyTimes() s.mdnsService.EXPECT().UnannounceMdnsEntry().AnyTimes() + s.mdnsService.EXPECT().RegisterMdnsSearch(gomock.Any()).AnyTimes() s.mdnsService.EXPECT().UnregisterMdnsSearch(gomock.Any()).AnyTimes() } @@ -83,7 +88,10 @@ func (s *HubSuite) Test_IsRemoteSKIPaired() { assert.Equal(s.T(), false, paired) // mark it as connected, so mDNS is not triggered - sut.connections[ski] = &ship.ShipConnection{} + con := &ship.ShipConnection{ + RemoteSKI: ski, + } + sut.registerConnection(con) sut.RegisterRemoteSKI(ski, true) paired = sut.IsRemoteServiceForSKIPaired(ski) @@ -96,21 +104,139 @@ func (s *HubSuite) Test_IsRemoteSKIPaired() { assert.Equal(s.T(), false, paired) } -func (s *HubSuite) Test_CheckRestartMdnsSearch() { +func (s *HubSuite) Test_HandleConnecitonClosed() { sut := connectionsHub{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), + remoteServices: make(map[string]*ServiceDetails), + serviceProvider: s.serviceProvider, + } + ski := "test" + + con := &ship.ShipConnection{ + RemoteSKI: ski, + } + + sut.HandleConnectionClosed(con, false) + + sut.registerConnection(con) + + sut.HandleConnectionClosed(con, true) + + assert.Equal(s.T(), 0, len(sut.connections)) +} + +func (s *HubSuite) Test_Mdns() { + localService := ServiceDetails{ + DeviceType: model.DeviceTypeTypeElectricitySupplySystem, + } + sut := connectionsHub{ + connections: make(map[string]*ship.ShipConnection), + connectionAttemptCounter: make(map[string]int), + remoteServices: make(map[string]*ServiceDetails), + localService: &localService, + mdns: s.mdnsService, + serviceProvider: s.serviceProvider, } sut.checkRestartMdnsSearch() - // Nothing to verify yet + + pairedServices := sut.numberPairedServices() + assert.Equal(s.T(), 0, len(sut.connections)) + assert.Equal(s.T(), 0, pairedServices) + + ski := "testski" + + sut.RegisterRemoteSKI(ski, true) + pairedServices = sut.numberPairedServices() + assert.Equal(s.T(), 0, len(sut.connections)) + assert.Equal(s.T(), 1, pairedServices) + + sut.StartBrowseMdnsSearch() + + sut.StopBrowseMdnsSearch() } -func (s *HubSuite) Test_ReportServiceShipID() { +func (s *HubSuite) Test_Ship() { + localService := ServiceDetails{ + DeviceType: model.DeviceTypeTypeElectricitySupplySystem, + } sut := connectionsHub{ - serviceProvider: s.serviceProvider, + connections: make(map[string]*ship.ShipConnection), + connectionAttemptCounter: make(map[string]int), + remoteServices: make(map[string]*ServiceDetails), + localService: &localService, + mdns: s.mdnsService, + serviceProvider: s.serviceProvider, + } + + ski := "testski" + + sut.HandleShipHandshakeStateUpdate(ski, ship.ShipState{ + State: ship.SmeStateError, + Error: errors.New("test"), + }) + + sut.HandleShipHandshakeStateUpdate(ski, ship.ShipState{ + State: ship.SmeHelloStateOk, + }) + + sut.ReportServiceShipID(ski, "test") + + trust := sut.AllowWaitingForTrust(ski) + assert.Equal(s.T(), true, trust) + + trust = sut.AllowWaitingForTrust("test") + assert.Equal(s.T(), false, trust) + + detail := sut.PairingDetailForSki(ski) + assert.NotNil(s.T(), detail) + + con := &ship.ShipConnection{ + RemoteSKI: ski, } - sut.ReportServiceShipID("", "") - // Nothing to verify yet + sut.registerConnection(con) + + detail = sut.PairingDetailForSki(ski) + assert.NotNil(s.T(), detail) +} + +func (s *HubSuite) Test_MapShipMessageExchangeState() { + sut := connectionsHub{} + + ski := "test" + + state := sut.mapShipMessageExchangeState(ship.CmiStateInitStart, ski) + assert.Equal(s.T(), ConnectionStateQueued, state) + + state = sut.mapShipMessageExchangeState(ship.CmiStateClientSend, ski) + assert.Equal(s.T(), ConnectionStateInitiated, state) + + state = sut.mapShipMessageExchangeState(ship.SmeHelloStateReadyInit, ski) + assert.Equal(s.T(), ConnectionStateInProgress, state) + + state = sut.mapShipMessageExchangeState(ship.SmeHelloStatePendingInit, ski) + assert.Equal(s.T(), ConnectionStateReceivedPairingRequest, state) + + state = sut.mapShipMessageExchangeState(ship.SmeHelloStateOk, ski) + assert.Equal(s.T(), ConnectionStateTrusted, state) + + state = sut.mapShipMessageExchangeState(ship.SmeHelloStateAbort, ski) + assert.Equal(s.T(), ConnectionStateNone, state) + + state = sut.mapShipMessageExchangeState(ship.SmePinStateCheckInit, ski) + assert.Equal(s.T(), ConnectionStatePin, state) + + state = sut.mapShipMessageExchangeState(ship.SmeAccessMethodsRequest, ski) + assert.Equal(s.T(), ConnectionStateInProgress, state) + + state = sut.mapShipMessageExchangeState(ship.SmeStateComplete, ski) + assert.Equal(s.T(), ConnectionStateCompleted, state) + + state = sut.mapShipMessageExchangeState(ship.SmeStateError, ski) + assert.Equal(s.T(), ConnectionStateError, state) + + state = sut.mapShipMessageExchangeState(ship.SmeProtHStateTimeout, ski) + assert.Equal(s.T(), ConnectionStateInProgress, state) } func (s *HubSuite) Test_DisconnectSKI() { @@ -225,3 +351,48 @@ func (s *HubSuite) Test_ConnectionAttemptRunning() { status = sut.isConnectionAttemptRunning(ski) assert.Equal(s.T(), false, status) } + +func (s *HubSuite) Test_InitiatePairingWithSKI() { + ski := "test" + sut := connectionsHub{ + connections: make(map[string]*ship.ShipConnection), + connectionAttemptRunning: make(map[string]bool), + remoteServices: make(map[string]*ServiceDetails), + serviceProvider: s.serviceProvider, + mdns: s.mdnsService, + } + + sut.InitiatePairingWithSKI(ski) + assert.Equal(s.T(), 0, len(sut.connections)) + + con := &ship.ShipConnection{ + RemoteSKI: ski, + } + sut.registerConnection(con) + sut.InitiatePairingWithSKI(ski) + assert.Equal(s.T(), 1, len(sut.connections)) +} + +func (s *HubSuite) Test_CancelPairingWithSKI() { + ski := "test" + sut := connectionsHub{ + connections: make(map[string]*ship.ShipConnection), + connectionAttemptRunning: make(map[string]bool), + remoteServices: make(map[string]*ServiceDetails), + serviceProvider: s.serviceProvider, + mdns: s.mdnsService, + } + + sut.CancelPairingWithSKI(ski) + assert.Equal(s.T(), 0, len(sut.connections)) + assert.Equal(s.T(), 0, len(sut.connectionAttemptRunning)) + + con := &ship.ShipConnection{ + RemoteSKI: ski, + } + sut.registerConnection(con) + assert.Equal(s.T(), 1, len(sut.connections)) + + sut.CancelPairingWithSKI(ski) + assert.Equal(s.T(), 0, len(sut.connectionAttemptRunning)) +} From c7077cab1d0efd043ae1f6483eaa80f86b13ddfe Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Jan 2024 12:41:51 +0100 Subject: [PATCH 100/240] Add service mdns tests --- service/mdns/mocks/MdnsProvider.go | 79 ++++++++++++ service/mdns/types.go | 2 + service/mdns_test.go | 191 +++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 service/mdns/mocks/MdnsProvider.go create mode 100644 service/mdns_test.go diff --git a/service/mdns/mocks/MdnsProvider.go b/service/mdns/mocks/MdnsProvider.go new file mode 100644 index 00000000..26a3740d --- /dev/null +++ b/service/mdns/mocks/MdnsProvider.go @@ -0,0 +1,79 @@ +// Code generated by mockery v2.39.1. DO NOT EDIT. + +package mocks + +import ( + net "net" + + mock "github.com/stretchr/testify/mock" +) + +// MdnsProvider is an autogenerated mock type for the MdnsProvider type +type MdnsProvider struct { + mock.Mock +} + +// Announce provides a mock function with given fields: serviceName, port, txt +func (_m *MdnsProvider) Announce(serviceName string, port int, txt []string) error { + ret := _m.Called(serviceName, port, txt) + + if len(ret) == 0 { + panic("no return value specified for Announce") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, int, []string) error); ok { + r0 = rf(serviceName, port, txt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CheckAvailability provides a mock function with given fields: +func (_m *MdnsProvider) CheckAvailability() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for CheckAvailability") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// ResolveEntries provides a mock function with given fields: cancelChan, callback +func (_m *MdnsProvider) ResolveEntries(cancelChan chan bool, callback func(map[string]string, string, string, []net.IP, int, bool)) { + _m.Called(cancelChan, callback) +} + +// Shutdown provides a mock function with given fields: +func (_m *MdnsProvider) Shutdown() { + _m.Called() +} + +// Unannounce provides a mock function with given fields: +func (_m *MdnsProvider) Unannounce() { + _m.Called() +} + +// NewMdnsProvider creates a new instance of MdnsProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMdnsProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *MdnsProvider { + mock := &MdnsProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/service/mdns/types.go b/service/mdns/types.go index b2cbd86b..97b520d2 100644 --- a/service/mdns/types.go +++ b/service/mdns/types.go @@ -5,6 +5,8 @@ import "net" const shipZeroConfServiceType = "_ship._tcp" const shipZeroConfDomain = "local." +//go:generate mockery --name=MdnsProvider + type MdnsProvider interface { CheckAvailability() bool Shutdown() diff --git a/service/mdns_test.go b/service/mdns_test.go new file mode 100644 index 00000000..becfbfb5 --- /dev/null +++ b/service/mdns_test.go @@ -0,0 +1,191 @@ +package service + +import ( + "crypto/tls" + "net" + "testing" + "time" + + mdnsmocks "github.com/enbility/eebus-go/service/mdns/mocks" + "github.com/enbility/eebus-go/spine/model" + gomock "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestMdnsSuite(t *testing.T) { + suite.Run(t, new(MdnsSuite)) +} + +type MdnsSuite struct { + suite.Suite + + sut *mdnsManager + + config *Configuration + + mdnsService *MockMdnsService + mdnsSearch *MockMdnsSearch + mdnsProvider *mdnsmocks.MdnsProvider +} + +func (s *MdnsSuite) SetupSuite() {} +func (s *MdnsSuite) TearDownTest() {} + +func (s *MdnsSuite) BeforeTest(suiteName, testName string) { + ctrl := gomock.NewController(s.T()) + + s.mdnsService = NewMockMdnsService(ctrl) + + s.mdnsSearch = NewMockMdnsSearch(ctrl) + s.mdnsSearch.EXPECT().ReportMdnsEntries(gomock.Any()).AnyTimes() + + s.mdnsProvider = mdnsmocks.NewMdnsProvider(s.T()) + s.mdnsProvider.On("ResolveEntries", mock.Anything, mock.Anything).Maybe().Return() + s.mdnsProvider.On("Shutdown").Maybe().Return() + + certificate := tls.Certificate{} + + s.config, _ = NewConfiguration( + "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, 4729, certificate, 230.0, time.Second*4) + + s.sut = newMDNS("test", s.config) + s.sut.mdnsProvider = s.mdnsProvider +} + +func (s *MdnsSuite) Test_SetupMdnsService() { + err := s.sut.SetupMdnsService() + assert.Nil(s.T(), err) + + assert.Equal(s.T(), true, s.sut.isAnnounced) + + s.sut.UnannounceMdnsEntry() + assert.Equal(s.T(), false, s.sut.isAnnounced) + + s.sut.UnannounceMdnsEntry() + assert.Equal(s.T(), false, s.sut.isAnnounced) + + ifaces, err := net.Interfaces() + assert.NotEqual(s.T(), 0, len(ifaces)) + assert.Nil(s.T(), err) + + s.config.interfaces = []string{ifaces[0].Name} + err = s.sut.SetupMdnsService() + assert.Nil(s.T(), err) + + s.config.interfaces = []string{"noifacename"} + err = s.sut.SetupMdnsService() + assert.NotNil(s.T(), err) + + assert.Equal(s.T(), false, s.sut.isSearchingServices) + s.sut.setIsSearchingServices(true) + assert.Equal(s.T(), true, s.sut.isSearchingServices) + +} + +func (s *MdnsSuite) Test_ShutdownMdnsService() { + s.sut.ShutdownMdnsService() + assert.Nil(s.T(), s.sut.mdnsProvider) +} + +func (s *MdnsSuite) Test_MdnsEntry() { + testSki := "test" + + entries := s.sut.mdnsEntries() + assert.Equal(s.T(), 0, len(entries)) + + entry := &MdnsEntry{ + Ski: testSki, + } + + s.sut.setMdnsEntry(testSki, entry) + entries = s.sut.mdnsEntries() + assert.Equal(s.T(), 1, len(entries)) + + theEntry, ok := s.sut.mdnsEntry(testSki) + assert.Equal(s.T(), true, ok) + assert.NotNil(s.T(), theEntry) + + copyEntries := s.sut.copyMdnsEntries() + assert.Equal(s.T(), 1, len(copyEntries)) + + s.sut.removeMdnsEntry(testSki) + entries = s.sut.mdnsEntries() + assert.Equal(s.T(), 0, len(entries)) + assert.Equal(s.T(), 1, len(copyEntries)) +} + +func (s *MdnsSuite) Test_MdnsSearch() { + assert.Equal(s.T(), false, s.sut.isSearchingServices) + s.sut.RegisterMdnsSearch(s.mdnsSearch) + assert.Equal(s.T(), true, s.sut.isSearchingServices) + + s.sut.setIsSearchingServices(true) + assert.Equal(s.T(), true, s.sut.isSearchingServices) + + s.sut.RegisterMdnsSearch(s.mdnsSearch) + + testSki := "test" + + entry := &MdnsEntry{ + Ski: testSki, + } + s.sut.setMdnsEntry(testSki, entry) + entries := s.sut.mdnsEntries() + assert.Equal(s.T(), 1, len(entries)) + + s.sut.setIsSearchingServices(false) + + s.sut.RegisterMdnsSearch(s.mdnsSearch) + + // wait a bit as ResolveEntries is called in a goroutine + time.Sleep(time.Millisecond * 200) + + s.sut.UnregisterMdnsSearch(s.mdnsSearch) +} + +func (s *MdnsSuite) Test_ProcessMdnsEntry() { + elements := make(map[string]string, 1) + + name := "name" + host := "host" + ips := []net.IP{} + port := 4567 + + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + elements["txtvers"] = "2" + elements["id"] = "id" + elements["path"] = "/ship" + elements["ski"] = "testski" + elements["register"] = "falsee" + + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + elements["txtvers"] = "1" + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + elements["ski"] = s.sut.ski + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + elements["ski"] = "testski" + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + elements["register"] = "false" + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + elements["brand"] = "brand" + elements["type"] = "type" + elements["model"] = "model" + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + ips = []net.IP{[]byte("127.0.0.1")} + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + s.sut.searchDelegate = s.mdnsSearch + s.sut.processMdnsEntry(elements, name, host, ips, port, false) + + s.sut.processMdnsEntry(elements, name, host, ips, port, true) +} From 2c133ad386945c2a097b561d404a4a96eb89ba33 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Jan 2024 12:55:37 +0100 Subject: [PATCH 101/240] Ignore network hostnames test on CI --- service/mdns_test.go | 10 +++++++--- ship/helper_test.go | 9 --------- ship/hs_hello_test.go | 4 ++-- ship/websocket_test.go | 3 ++- util/helper.go | 6 ++++++ 5 files changed, 17 insertions(+), 15 deletions(-) delete mode 100644 ship/helper_test.go diff --git a/service/mdns_test.go b/service/mdns_test.go index becfbfb5..f21085f0 100644 --- a/service/mdns_test.go +++ b/service/mdns_test.go @@ -8,6 +8,7 @@ import ( mdnsmocks "github.com/enbility/eebus-go/service/mdns/mocks" "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -71,9 +72,12 @@ func (s *MdnsSuite) Test_SetupMdnsService() { assert.NotEqual(s.T(), 0, len(ifaces)) assert.Nil(s.T(), err) - s.config.interfaces = []string{ifaces[0].Name} - err = s.sut.SetupMdnsService() - assert.Nil(s.T(), err) + // we don't have access to iface names on CI + if !util.IsRunningOnCI() { + s.config.interfaces = []string{ifaces[0].Name} + err = s.sut.SetupMdnsService() + assert.Nil(s.T(), err) + } s.config.interfaces = []string{"noifacename"} err = s.sut.SetupMdnsService() diff --git a/ship/helper_test.go b/ship/helper_test.go deleted file mode 100644 index ba860071..00000000 --- a/ship/helper_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package ship - -import ( - "os" -) - -func isRunningOnCI() bool { - return os.Getenv("ACTION_ENVIRONMENT") == "CI" -} diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go index db82e657..40aa874d 100644 --- a/ship/hs_hello_test.go +++ b/ship/hs_hello_test.go @@ -75,7 +75,7 @@ func (s *HelloSuite) Test_ReadyListen_Timeout() { sut.setState(SmeHelloStateReadyInit, nil) // inits the timer sut.setState(SmeHelloStateReadyListen, nil) - if !isRunningOnCI() { + if !util.IsRunningOnCI() { // test if the function is triggered correctly via the timer time.Sleep(tHelloInit + time.Second) } else { @@ -192,7 +192,7 @@ func (s *HelloSuite) Test_PendingListen_Timeout() { sut.setState(SmeHelloStatePendingInit, nil) // inits the timer sut.setState(SmeHelloStatePendingListen, nil) - if !isRunningOnCI() { + if !util.IsRunningOnCI() { // test if the function is triggered correctly via the timer time.Sleep(tHelloInit + time.Second) } else { diff --git a/ship/websocket_test.go b/ship/websocket_test.go index 8a0fe5f3..78afea90 100644 --- a/ship/websocket_test.go +++ b/ship/websocket_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + util "github.com/enbility/eebus-go/util" "github.com/golang/mock/gomock" "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" @@ -111,7 +112,7 @@ func (s *WebsocketSuite) TestPingPeriod() { assert.Equal(s.T(), false, isClosed) assert.Nil(s.T(), err) - if !isRunningOnCI() { + if !util.IsRunningOnCI() { // test if the function is triggered correctly via the timer time.Sleep(time.Second * 51) } else { diff --git a/util/helper.go b/util/helper.go index f02ab959..ec095f12 100644 --- a/util/helper.go +++ b/util/helper.go @@ -2,6 +2,7 @@ package util import ( "encoding/json" + "os" "strings" ) @@ -19,3 +20,8 @@ func DeepCopy[A any](source, dest A) { byt, _ := json.Marshal(source) _ = json.Unmarshal(byt, dest) } + +// used in tests +func IsRunningOnCI() bool { + return os.Getenv("ACTION_ENVIRONMENT") == "CI" +} From 9f5855f2ceba2fda2c738487cfd37d1c62d61b2e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Jan 2024 12:59:53 +0100 Subject: [PATCH 102/240] Add test assertions --- service/mdns_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/service/mdns_test.go b/service/mdns_test.go index f21085f0..6393f843 100644 --- a/service/mdns_test.go +++ b/service/mdns_test.go @@ -159,6 +159,7 @@ func (s *MdnsSuite) Test_ProcessMdnsEntry() { port := 4567 s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) elements["txtvers"] = "2" elements["id"] = "id" @@ -167,29 +168,38 @@ func (s *MdnsSuite) Test_ProcessMdnsEntry() { elements["register"] = "falsee" s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) elements["txtvers"] = "1" s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) elements["ski"] = s.sut.ski s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) elements["ski"] = "testski" s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) elements["register"] = "false" s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 1, len(s.sut.mdnsEntries())) elements["brand"] = "brand" elements["type"] = "type" elements["model"] = "model" s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 1, len(s.sut.mdnsEntries())) ips = []net.IP{[]byte("127.0.0.1")} s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 1, len(s.sut.mdnsEntries())) s.sut.searchDelegate = s.mdnsSearch s.sut.processMdnsEntry(elements, name, host, ips, port, false) + assert.Equal(s.T(), 1, len(s.sut.mdnsEntries())) s.sut.processMdnsEntry(elements, name, host, ips, port, true) + assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) } From 81705cd5e2f83320b292f52823b9f921cabcbc14 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Jan 2024 14:10:27 +0100 Subject: [PATCH 103/240] Add tests for service --- logging/log.go | 2 + logging/mocks/Logging.go | 84 ++++++++++++++++++++ service/hub.go | 123 ++++++++++++++++------------- service/hub_test.go | 30 +++---- service/mock_hub_test.go | 149 ++++++++++++++++++++++++++++++++++- service/mock_service_test.go | 108 +++++++++++++++++++++++++ service/service.go | 25 +++--- service/service_test.go | 147 ++++++++++++++++++++++++++++++++++ 8 files changed, 588 insertions(+), 80 deletions(-) create mode 100644 logging/mocks/Logging.go create mode 100644 service/mock_service_test.go create mode 100644 service/service_test.go diff --git a/logging/log.go b/logging/log.go index e65ab284..7ddb1611 100644 --- a/logging/log.go +++ b/logging/log.go @@ -1,5 +1,7 @@ package logging +//go:generate mockery --name=Logging + // Logging needs to be implemented, if the internal logs should be printed type Logging interface { Trace(args ...interface{}) diff --git a/logging/mocks/Logging.go b/logging/mocks/Logging.go new file mode 100644 index 00000000..d88ff663 --- /dev/null +++ b/logging/mocks/Logging.go @@ -0,0 +1,84 @@ +// Code generated by mockery v2.39.1. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// Logging is an autogenerated mock type for the Logging type +type Logging struct { + mock.Mock +} + +// Debug provides a mock function with given fields: args +func (_m *Logging) Debug(args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Debugf provides a mock function with given fields: format, args +func (_m *Logging) Debugf(format string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Error provides a mock function with given fields: args +func (_m *Logging) Error(args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Errorf provides a mock function with given fields: format, args +func (_m *Logging) Errorf(format string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Info provides a mock function with given fields: args +func (_m *Logging) Info(args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Infof provides a mock function with given fields: format, args +func (_m *Logging) Infof(format string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Trace provides a mock function with given fields: args +func (_m *Logging) Trace(args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Tracef provides a mock function with given fields: format, args +func (_m *Logging) Tracef(format string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// NewLogging creates a new instance of Logging. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewLogging(t interface { + mock.TestingT + Cleanup(func()) +}) *Logging { + mock := &Logging{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/service/hub.go b/service/hub.go index 3ef0c91a..2b2a73ce 100644 --- a/service/hub.go +++ b/service/hub.go @@ -40,7 +40,7 @@ var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ {min: 10, max: 20}, } -//go:generate mockgen -destination=mock_hub_test.go -package=service github.com/enbility/eebus-go/service ServiceProvider +//go:generate mockgen -destination=mock_hub_test.go -package=service github.com/enbility/eebus-go/service ServiceProvider,ConnectionsHub // interface for reporting data from connectionsHub to the EEBUSService type ServiceProvider interface { @@ -64,8 +64,21 @@ type ServiceProvider interface { AllowWaitingForTrust(ski string) bool } +type ConnectionsHub interface { + PairingDetailForSki(ski string) *ConnectionStateDetail + StartBrowseMdnsSearch() + StopBrowseMdnsSearch() + Start() + Shutdown() + ServiceForSKI(ski string) *ServiceDetails + RegisterRemoteSKI(ski string, enable bool) + InitiatePairingWithSKI(ski string) + CancelPairingWithSKI(ski string) + DisconnectSKI(ski string, reason string) +} + // handling all connections to remote services -type connectionsHub struct { +type connectionsHubImpl struct { connections map[string]*ship.ShipConnection // which attempt is it to initate an connection to the remote SKI @@ -98,8 +111,8 @@ type connectionsHub struct { muxMdns sync.Mutex } -func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice *spine.DeviceLocalImpl, configuration *Configuration, localService *ServiceDetails) *connectionsHub { - hub := &connectionsHub{ +func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice *spine.DeviceLocalImpl, configuration *Configuration, localService *ServiceDetails) ConnectionsHub { + hub := &connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), @@ -115,8 +128,8 @@ func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineL return hub } -// start the ConnectionsHub with all its services -func (h *connectionsHub) start() { +// Start the ConnectionsHub with all its services +func (h *connectionsHubImpl) Start() { // start the websocket server if err := h.startWebsocketServer(); err != nil { logging.Log.Debug("error during websocket server starting:", err) @@ -131,17 +144,17 @@ func (h *connectionsHub) start() { h.checkRestartMdnsSearch() } -var _ ship.ShipServiceDataProvider = (*connectionsHub)(nil) +var _ ship.ShipServiceDataProvider = (*connectionsHubImpl)(nil) // Returns if the provided SKI is from a registered service -func (h *connectionsHub) IsRemoteServiceForSKIPaired(ski string) bool { - service := h.serviceForSKI(ski) +func (h *connectionsHubImpl) IsRemoteServiceForSKIPaired(ski string) bool { + service := h.ServiceForSKI(ski) return service.Trusted } // The connection was closed, we need to clean up -func (h *connectionsHub) HandleConnectionClosed(connection *ship.ShipConnection, handshakeCompleted bool) { +func (h *connectionsHubImpl) HandleConnectionClosed(connection *ship.ShipConnection, handshakeCompleted bool) { // only remove this connection if it is the registered one for the ski! // as we can have double connections but only one can be registered if existingC := h.connectionForSKI(connection.RemoteSKI); existingC != nil { @@ -160,7 +173,7 @@ func (h *connectionsHub) HandleConnectionClosed(connection *ship.ShipConnection, h.serviceProvider.RemoteSKIDisconnected(connection.RemoteSKI) // Do not automatically reconnect if handshake failed and not already paired - remoteService := h.serviceForSKI(connection.RemoteSKI) + remoteService := h.ServiceForSKI(connection.RemoteSKI) if !handshakeCompleted && !remoteService.Trusted { return } @@ -169,7 +182,7 @@ func (h *connectionsHub) HandleConnectionClosed(connection *ship.ShipConnection, } // return the number of paired services -func (h *connectionsHub) numberPairedServices() int { +func (h *connectionsHubImpl) numberPairedServices() int { amount := 0 h.muxReg.Lock() @@ -184,7 +197,7 @@ func (h *connectionsHub) numberPairedServices() int { } // startup mDNS if a paired service is not connected -func (h *connectionsHub) checkRestartMdnsSearch() { +func (h *connectionsHubImpl) checkRestartMdnsSearch() { countPairedServices := h.numberPairedServices() h.muxCon.Lock() countConnections := len(h.connections) @@ -200,26 +213,26 @@ func (h *connectionsHub) checkRestartMdnsSearch() { } } -func (h *connectionsHub) StartBrowseMdnsSearch() { +func (h *connectionsHubImpl) StartBrowseMdnsSearch() { // TODO: this currently collides with searching for a specific SKI h.mdns.RegisterMdnsSearch(h) } -func (h *connectionsHub) StopBrowseMdnsSearch() { +func (h *connectionsHubImpl) StopBrowseMdnsSearch() { // TODO: this currently collides with searching for a specific SKI h.mdns.UnregisterMdnsSearch(h) } // Provides the SHIP ID the remote service reported during the handshake process -func (h *connectionsHub) ReportServiceShipID(ski string, shipdID string) { +func (h *connectionsHubImpl) ReportServiceShipID(ski string, shipdID string) { h.serviceProvider.RemoteSKIConnected(ski) h.serviceProvider.ServiceShipIDUpdate(ski, shipdID) } // return if the user is still able to trust the connection -func (h *connectionsHub) AllowWaitingForTrust(ski string) bool { - if service := h.serviceForSKI(ski); service != nil { +func (h *connectionsHubImpl) AllowWaitingForTrust(ski string) bool { + if service := h.ServiceForSKI(ski); service != nil { if service.Trusted { return true } @@ -229,7 +242,7 @@ func (h *connectionsHub) AllowWaitingForTrust(ski string) bool { } // Provides the current ship message exchange state for a given SKI and the corresponding error if state is error -func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.ShipState) { +func (h *connectionsHubImpl) HandleShipHandshakeStateUpdate(ski string, state ship.ShipState) { // overwrite service Paired value if state.State == ship.SmeHelloStateOk { h.RegisterRemoteSKI(ski, true) @@ -242,7 +255,7 @@ func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.S pairingDetail := NewConnectionStateDetail(pairingState, state.Error) - service := h.serviceForSKI(ski) + service := h.ServiceForSKI(ski) existingDetails := service.ConnectionStateDetail if existingDetails.State() != pairingState || existingDetails.Error() != state.Error { @@ -258,8 +271,8 @@ func (h *connectionsHub) HandleShipHandshakeStateUpdate(ski string, state ship.S // // ErrNotPaired if the SKI is not in the (to be) paired list // ErrNoConnectionFound if no connection for the SKI was found -func (h *connectionsHub) PairingDetailForSki(ski string) *ConnectionStateDetail { - service := h.serviceForSKI(ski) +func (h *connectionsHubImpl) PairingDetailForSki(ski string) *ConnectionStateDetail { + service := h.ServiceForSKI(ski) if conn := h.connectionForSKI(ski); conn != nil { shipState, shipError := conn.ShipHandshakeState() @@ -271,7 +284,7 @@ func (h *connectionsHub) PairingDetailForSki(ski string) *ConnectionStateDetail } // maps ShipMessageExchangeState to PairingState -func (h *connectionsHub) mapShipMessageExchangeState(state ship.ShipMessageExchangeState, ski string) ConnectionState { +func (h *connectionsHubImpl) mapShipMessageExchangeState(state ship.ShipMessageExchangeState, ski string) ConnectionState { var connState ConnectionState // map the SHIP states to a public gState @@ -311,7 +324,7 @@ func (h *connectionsHub) mapShipMessageExchangeState(state ship.ShipMessageExcha // Disconnect a connection to an SKI, used by a service implementation // e.g. if heartbeats go wrong -func (h *connectionsHub) DisconnectSKI(ski string, reason string) { +func (h *connectionsHubImpl) DisconnectSKI(ski string, reason string) { h.muxCon.Lock() defer h.muxCon.Unlock() @@ -325,14 +338,14 @@ func (h *connectionsHub) DisconnectSKI(ski string, reason string) { } // register a new ship Connection -func (h *connectionsHub) registerConnection(connection *ship.ShipConnection) { +func (h *connectionsHubImpl) registerConnection(connection *ship.ShipConnection) { h.muxCon.Lock() h.connections[connection.RemoteSKI] = connection h.muxCon.Unlock() } // return the connection for a specific SKI -func (h *connectionsHub) connectionForSKI(ski string) *ship.ShipConnection { +func (h *connectionsHubImpl) connectionForSKI(ski string) *ship.ShipConnection { h.muxCon.Lock() defer h.muxCon.Unlock() @@ -344,7 +357,7 @@ func (h *connectionsHub) connectionForSKI(ski string) *ship.ShipConnection { } // close all connections -func (h *connectionsHub) shutdown() { +func (h *connectionsHubImpl) Shutdown() { h.mdns.ShutdownMdnsService() for _, c := range h.connections { c.CloseConnection(false, 0, "") @@ -352,7 +365,7 @@ func (h *connectionsHub) shutdown() { } // return if there is a connection for a SKI -func (h *connectionsHub) isSkiConnected(ski string) bool { +func (h *connectionsHubImpl) isSkiConnected(ski string) bool { h.muxCon.Lock() defer h.muxCon.Unlock() @@ -364,7 +377,7 @@ func (h *connectionsHub) isSkiConnected(ski string) bool { // Websocket connection handling // start the ship websocket server -func (h *connectionsHub) startWebsocketServer() error { +func (h *connectionsHubImpl) startWebsocketServer() error { addr := fmt.Sprintf(":%d", h.configuration.port) logging.Log.Debug("starting websocket server on", addr) @@ -410,7 +423,7 @@ func (h *connectionsHub) startWebsocketServer() error { // Connection Handling // HTTP Server callback for handling incoming connection requests -func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { upgrader := websocket.Upgrader{ ReadBufferSize: ship.MaxMessageSize, WriteBufferSize: ship.MaxMessageSize, @@ -450,7 +463,7 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { logging.Log.Debug("incoming connection request from", remoteService.SKI) // Check if the remote service is paired - service := h.serviceForSKI(remoteService.SKI) + service := h.ServiceForSKI(remoteService.SKI) if service.ConnectionStateDetail.State() == ConnectionStateQueued { service.ConnectionStateDetail.SetState(ConnectionStateReceivedPairingRequest) h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) @@ -475,7 +488,7 @@ func (h *connectionsHub) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Connect to another EEBUS service // // returns error contains a reason for failing the connection or nil if no further tries should be processed -func (h *connectionsHub) connectFoundService(remoteService *ServiceDetails, host, port string) error { +func (h *connectionsHubImpl) connectFoundService(remoteService *ServiceDetails, host, port string) error { if h.isSkiConnected(remoteService.SKI) { return nil } @@ -544,7 +557,7 @@ func (h *connectionsHub) connectFoundService(remoteService *ServiceDetails, host // // returns true if this connection is fine to be continue // returns false if this connection should not be established or kept -func (h *connectionsHub) keepThisConnection(conn *websocket.Conn, incomingRequest bool, remoteService *ServiceDetails) bool { +func (h *connectionsHubImpl) keepThisConnection(conn *websocket.Conn, incomingRequest bool, remoteService *ServiceDetails) bool { // SHIP 12.2.2 defines: // prevent double connections with SKI Comparison // the node with the hight SKI value kees the most recent connection and @@ -588,7 +601,7 @@ func (h *connectionsHub) keepThisConnection(conn *websocket.Conn, incomingReques } // return the service for a given SKI or an error if not found -func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { +func (h *connectionsHubImpl) ServiceForSKI(ski string) *ServiceDetails { h.muxReg.Lock() defer h.muxReg.Unlock() @@ -605,8 +618,8 @@ func (h *connectionsHub) serviceForSKI(ski string) *ServiceDetails { // Sets the SKI as being paired or not // Should be used for services which completed the pairing process and // which were stored as having the process completed -func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { - service := h.serviceForSKI(ski) +func (h *connectionsHubImpl) RegisterRemoteSKI(ski string, enable bool) { + service := h.ServiceForSKI(ski) service.Trusted = enable if enable { @@ -626,7 +639,7 @@ func (h *connectionsHub) RegisterRemoteSKI(ski string, enable bool) { } // Triggers the pairing process for a SKI -func (h *connectionsHub) InitiatePairingWithSKI(ski string) { +func (h *connectionsHubImpl) InitiatePairingWithSKI(ski string) { conn := h.connectionForSKI(ski) // remotely initiated @@ -637,7 +650,7 @@ func (h *connectionsHub) InitiatePairingWithSKI(ski string) { } // locally initiated - service := h.serviceForSKI(ski) + service := h.ServiceForSKI(ski) service.ConnectionStateDetail.SetState(ConnectionStateQueued) h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) @@ -649,14 +662,14 @@ func (h *connectionsHub) InitiatePairingWithSKI(ski string) { } // Cancels the pairing process for a SKI -func (h *connectionsHub) CancelPairingWithSKI(ski string) { +func (h *connectionsHubImpl) CancelPairingWithSKI(ski string) { h.removeConnectionAttemptCounter(ski) if existingC := h.connectionForSKI(ski); existingC != nil { existingC.AbortPendingHandshake() } - service := h.serviceForSKI(ski) + service := h.ServiceForSKI(ski) service.ConnectionStateDetail.SetState(ConnectionStateNone) service.Trusted = false @@ -664,7 +677,7 @@ func (h *connectionsHub) CancelPairingWithSKI(ski string) { } // Process reported mDNS services -func (h *connectionsHub) ReportMdnsEntries(entries map[string]*MdnsEntry) { +func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*MdnsEntry) { h.muxMdns.Lock() defer h.muxMdns.Unlock() @@ -679,7 +692,7 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]*MdnsEntry) { } // Check if the remote service is paired or queued for connection - service := h.serviceForSKI(ski) + service := h.ServiceForSKI(ski) if !h.IsRemoteServiceForSKIPaired(ski) && service.ConnectionStateDetail.State() != ConnectionStateQueued { continue @@ -709,7 +722,7 @@ func (h *connectionsHub) ReportMdnsEntries(entries map[string]*MdnsEntry) { } // coordinate connection initiation attempts to a remove service -func (h *connectionsHub) coordinateConnectionInitations(ski string, entry *MdnsEntry) { +func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *MdnsEntry) { if h.isConnectionAttemptRunning(ski) { return } @@ -718,7 +731,7 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry *MdnsE counter, duration := h.getConnectionInitiationDelayTime(ski) - service := h.serviceForSKI(ski) + service := h.ServiceForSKI(ski) if service.ConnectionStateDetail.State() == ConnectionStateQueued { go h.prepareConnectionInitation(ski, counter, entry) return @@ -738,7 +751,7 @@ func (h *connectionsHub) coordinateConnectionInitations(ski string, entry *MdnsE // invoked by coordinateConnectionInitations either with a delay or directly // when initating a pairing process -func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, entry *MdnsEntry) { +func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, entry *MdnsEntry) { h.setConnectionAttemptRunning(ski, false) // check if the current counter is still the same, otherwise this counter is irrelevant @@ -749,7 +762,7 @@ func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, ent // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing - pairingState := h.serviceForSKI(ski).ConnectionStateDetail.State() + pairingState := h.ServiceForSKI(ski).ConnectionStateDetail.State() if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != ConnectionStateQueued { return } @@ -761,7 +774,7 @@ func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, ent // now initiate the connection // check if the remoteService still exists - service := h.serviceForSKI(ski) + service := h.ServiceForSKI(ski) if success := h.initateConnection(service, entry); !success { h.checkRestartMdnsSearch() @@ -770,14 +783,14 @@ func (h *connectionsHub) prepareConnectionInitation(ski string, counter int, ent // attempt to establish a connection to a remote service // returns true if successful -func (h *connectionsHub) initateConnection(remoteService *ServiceDetails, entry *MdnsEntry) bool { +func (h *connectionsHubImpl) initateConnection(remoteService *ServiceDetails, entry *MdnsEntry) bool { var err error // try connecting via an IP address first for _, address := range entry.Addresses { // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing - pairingState := h.serviceForSKI(remoteService.SKI).ConnectionStateDetail.State() + pairingState := h.ServiceForSKI(remoteService.SKI).ConnectionStateDetail.State() if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != ConnectionStateQueued { return false } @@ -806,7 +819,7 @@ func (h *connectionsHub) initateConnection(remoteService *ServiceDetails, entry } // increase the connection attempt counter for the given ski -func (h *connectionsHub) increaseConnectionAttemptCounter(ski string) int { +func (h *connectionsHubImpl) increaseConnectionAttemptCounter(ski string) int { h.muxConAttempt.Lock() defer h.muxConAttempt.Unlock() @@ -825,7 +838,7 @@ func (h *connectionsHub) increaseConnectionAttemptCounter(ski string) int { } // remove the connection attempt counter for the given ski -func (h *connectionsHub) removeConnectionAttemptCounter(ski string) { +func (h *connectionsHubImpl) removeConnectionAttemptCounter(ski string) { h.muxConAttempt.Lock() defer h.muxConAttempt.Unlock() @@ -833,7 +846,7 @@ func (h *connectionsHub) removeConnectionAttemptCounter(ski string) { } // get the current attempt counter -func (h *connectionsHub) getCurrentConnectionAttemptCounter(ski string) (int, bool) { +func (h *connectionsHubImpl) getCurrentConnectionAttemptCounter(ski string) (int, bool) { h.muxConAttempt.Lock() defer h.muxConAttempt.Unlock() @@ -844,7 +857,7 @@ func (h *connectionsHub) getCurrentConnectionAttemptCounter(ski string) (int, bo // get the connection initiation delay time range for a given ski // returns the current counter and the duration -func (h *connectionsHub) getConnectionInitiationDelayTime(ski string) (int, time.Duration) { +func (h *connectionsHubImpl) getConnectionInitiationDelayTime(ski string) (int, time.Duration) { counter := h.increaseConnectionAttemptCounter(ski) h.muxConAttempt.Lock() @@ -872,7 +885,7 @@ func (h *connectionsHub) getConnectionInitiationDelayTime(ski string) (int, time } // set if a connection attempt is running/in progress -func (h *connectionsHub) setConnectionAttemptRunning(ski string, active bool) { +func (h *connectionsHubImpl) setConnectionAttemptRunning(ski string, active bool) { h.muxConAttempt.Lock() defer h.muxConAttempt.Unlock() @@ -880,7 +893,7 @@ func (h *connectionsHub) setConnectionAttemptRunning(ski string, active bool) { } // return if a connection attempt is runnning/in progress -func (h *connectionsHub) isConnectionAttemptRunning(ski string) bool { +func (h *connectionsHubImpl) isConnectionAttemptRunning(ski string) bool { h.muxConAttempt.Lock() defer h.muxConAttempt.Unlock() diff --git a/service/hub_test.go b/service/hub_test.go index 17ec46dd..06b32818 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -72,11 +72,11 @@ func (s *HubSuite) Test_NewConnectionsHub() { hub := newConnectionsHub(s.serviceProvider, s.mdnsService, nil, configuration, localService) assert.NotNil(s.T(), hub) - hub.start() + hub.Start() } func (s *HubSuite) Test_IsRemoteSKIPaired() { - sut := connectionsHub{ + sut := connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), remoteServices: make(map[string]*ServiceDetails), @@ -105,7 +105,7 @@ func (s *HubSuite) Test_IsRemoteSKIPaired() { } func (s *HubSuite) Test_HandleConnecitonClosed() { - sut := connectionsHub{ + sut := connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), remoteServices: make(map[string]*ServiceDetails), @@ -130,7 +130,7 @@ func (s *HubSuite) Test_Mdns() { localService := ServiceDetails{ DeviceType: model.DeviceTypeTypeElectricitySupplySystem, } - sut := connectionsHub{ + sut := connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), remoteServices: make(map[string]*ServiceDetails), @@ -160,7 +160,7 @@ func (s *HubSuite) Test_Ship() { localService := ServiceDetails{ DeviceType: model.DeviceTypeTypeElectricitySupplySystem, } - sut := connectionsHub{ + sut := connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), remoteServices: make(map[string]*ServiceDetails), @@ -201,7 +201,7 @@ func (s *HubSuite) Test_Ship() { } func (s *HubSuite) Test_MapShipMessageExchangeState() { - sut := connectionsHub{} + sut := connectionsHubImpl{} ski := "test" @@ -240,7 +240,7 @@ func (s *HubSuite) Test_MapShipMessageExchangeState() { } func (s *HubSuite) Test_DisconnectSKI() { - sut := connectionsHub{ + sut := connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), } ski := "test" @@ -251,7 +251,7 @@ func (s *HubSuite) Test_RegisterConnection() { ski := "12af9e" localService := NewServiceDetails(ski) - sut := connectionsHub{ + sut := connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), mdns: s.mdnsService, localService: localService, @@ -270,7 +270,7 @@ func (s *HubSuite) Test_RegisterConnection() { func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { // we just need a dummy for this test - sut := connectionsHub{ + sut := connectionsHubImpl{ connectionAttemptCounter: make(map[string]int), } ski := "test" @@ -291,7 +291,7 @@ func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { func (s *HubSuite) Test_RemoveConnectionAttemptCounter() { // we just need a dummy for this test - sut := connectionsHub{ + sut := connectionsHubImpl{ connectionAttemptCounter: make(map[string]int), } ski := "test" @@ -307,7 +307,7 @@ func (s *HubSuite) Test_RemoveConnectionAttemptCounter() { func (s *HubSuite) Test_GetCurrentConnectionAttemptCounter() { // we just need a dummy for this test - sut := connectionsHub{ + sut := connectionsHubImpl{ connectionAttemptCounter: make(map[string]int), } ski := "test" @@ -326,7 +326,7 @@ func (s *HubSuite) Test_GetConnectionInitiationDelayTime() { // we just need a dummy for this test ski := "12af9e" localService := NewServiceDetails(ski) - sut := connectionsHub{ + sut := connectionsHubImpl{ localService: localService, connectionAttemptCounter: make(map[string]int), } @@ -340,7 +340,7 @@ func (s *HubSuite) Test_GetConnectionInitiationDelayTime() { func (s *HubSuite) Test_ConnectionAttemptRunning() { // we just need a dummy for this test ski := "test" - sut := connectionsHub{ + sut := connectionsHubImpl{ connectionAttemptRunning: make(map[string]bool), } @@ -354,7 +354,7 @@ func (s *HubSuite) Test_ConnectionAttemptRunning() { func (s *HubSuite) Test_InitiatePairingWithSKI() { ski := "test" - sut := connectionsHub{ + sut := connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), connectionAttemptRunning: make(map[string]bool), remoteServices: make(map[string]*ServiceDetails), @@ -375,7 +375,7 @@ func (s *HubSuite) Test_InitiatePairingWithSKI() { func (s *HubSuite) Test_CancelPairingWithSKI() { ski := "test" - sut := connectionsHub{ + sut := connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), connectionAttemptRunning: make(map[string]bool), remoteServices: make(map[string]*ServiceDetails), diff --git a/service/mock_hub_test.go b/service/mock_hub_test.go index 99f8b912..fddaaff5 100644 --- a/service/mock_hub_test.go +++ b/service/mock_hub_test.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/enbility/eebus-go/service (interfaces: ServiceProvider) +// Source: github.com/enbility/eebus-go/service (interfaces: ServiceProvider,ConnectionsHub) // Package service is a generated GoMock package. package service @@ -106,3 +106,150 @@ func (mr *MockServiceProviderMockRecorder) VisibleMDNSRecordsUpdated(arg0 interf mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleMDNSRecordsUpdated", reflect.TypeOf((*MockServiceProvider)(nil).VisibleMDNSRecordsUpdated), arg0) } + +// MockConnectionsHub is a mock of ConnectionsHub interface. +type MockConnectionsHub struct { + ctrl *gomock.Controller + recorder *MockConnectionsHubMockRecorder +} + +// MockConnectionsHubMockRecorder is the mock recorder for MockConnectionsHub. +type MockConnectionsHubMockRecorder struct { + mock *MockConnectionsHub +} + +// NewMockConnectionsHub creates a new mock instance. +func NewMockConnectionsHub(ctrl *gomock.Controller) *MockConnectionsHub { + mock := &MockConnectionsHub{ctrl: ctrl} + mock.recorder = &MockConnectionsHubMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockConnectionsHub) EXPECT() *MockConnectionsHubMockRecorder { + return m.recorder +} + +// CancelPairingWithSKI mocks base method. +func (m *MockConnectionsHub) CancelPairingWithSKI(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "CancelPairingWithSKI", arg0) +} + +// CancelPairingWithSKI indicates an expected call of CancelPairingWithSKI. +func (mr *MockConnectionsHubMockRecorder) CancelPairingWithSKI(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelPairingWithSKI", reflect.TypeOf((*MockConnectionsHub)(nil).CancelPairingWithSKI), arg0) +} + +// DisconnectSKI mocks base method. +func (m *MockConnectionsHub) DisconnectSKI(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DisconnectSKI", arg0, arg1) +} + +// DisconnectSKI indicates an expected call of DisconnectSKI. +func (mr *MockConnectionsHubMockRecorder) DisconnectSKI(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisconnectSKI", reflect.TypeOf((*MockConnectionsHub)(nil).DisconnectSKI), arg0, arg1) +} + +// InitiatePairingWithSKI mocks base method. +func (m *MockConnectionsHub) InitiatePairingWithSKI(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "InitiatePairingWithSKI", arg0) +} + +// InitiatePairingWithSKI indicates an expected call of InitiatePairingWithSKI. +func (mr *MockConnectionsHubMockRecorder) InitiatePairingWithSKI(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitiatePairingWithSKI", reflect.TypeOf((*MockConnectionsHub)(nil).InitiatePairingWithSKI), arg0) +} + +// PairingDetailForSki mocks base method. +func (m *MockConnectionsHub) PairingDetailForSki(arg0 string) *ConnectionStateDetail { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PairingDetailForSki", arg0) + ret0, _ := ret[0].(*ConnectionStateDetail) + return ret0 +} + +// PairingDetailForSki indicates an expected call of PairingDetailForSki. +func (mr *MockConnectionsHubMockRecorder) PairingDetailForSki(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PairingDetailForSki", reflect.TypeOf((*MockConnectionsHub)(nil).PairingDetailForSki), arg0) +} + +// RegisterRemoteSKI mocks base method. +func (m *MockConnectionsHub) RegisterRemoteSKI(arg0 string, arg1 bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterRemoteSKI", arg0, arg1) +} + +// RegisterRemoteSKI indicates an expected call of RegisterRemoteSKI. +func (mr *MockConnectionsHubMockRecorder) RegisterRemoteSKI(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRemoteSKI", reflect.TypeOf((*MockConnectionsHub)(nil).RegisterRemoteSKI), arg0, arg1) +} + +// ServiceForSKI mocks base method. +func (m *MockConnectionsHub) ServiceForSKI(arg0 string) *ServiceDetails { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ServiceForSKI", arg0) + ret0, _ := ret[0].(*ServiceDetails) + return ret0 +} + +// ServiceForSKI indicates an expected call of ServiceForSKI. +func (mr *MockConnectionsHubMockRecorder) ServiceForSKI(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceForSKI", reflect.TypeOf((*MockConnectionsHub)(nil).ServiceForSKI), arg0) +} + +// Shutdown mocks base method. +func (m *MockConnectionsHub) Shutdown() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Shutdown") +} + +// Shutdown indicates an expected call of Shutdown. +func (mr *MockConnectionsHubMockRecorder) Shutdown() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockConnectionsHub)(nil).Shutdown)) +} + +// Start mocks base method. +func (m *MockConnectionsHub) Start() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Start") +} + +// Start indicates an expected call of Start. +func (mr *MockConnectionsHubMockRecorder) Start() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockConnectionsHub)(nil).Start)) +} + +// StartBrowseMdnsSearch mocks base method. +func (m *MockConnectionsHub) StartBrowseMdnsSearch() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "StartBrowseMdnsSearch") +} + +// StartBrowseMdnsSearch indicates an expected call of StartBrowseMdnsSearch. +func (mr *MockConnectionsHubMockRecorder) StartBrowseMdnsSearch() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartBrowseMdnsSearch", reflect.TypeOf((*MockConnectionsHub)(nil).StartBrowseMdnsSearch)) +} + +// StopBrowseMdnsSearch mocks base method. +func (m *MockConnectionsHub) StopBrowseMdnsSearch() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "StopBrowseMdnsSearch") +} + +// StopBrowseMdnsSearch indicates an expected call of StopBrowseMdnsSearch. +func (mr *MockConnectionsHubMockRecorder) StopBrowseMdnsSearch() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopBrowseMdnsSearch", reflect.TypeOf((*MockConnectionsHub)(nil).StopBrowseMdnsSearch)) +} diff --git a/service/mock_service_test.go b/service/mock_service_test.go new file mode 100644 index 00000000..9741f384 --- /dev/null +++ b/service/mock_service_test.go @@ -0,0 +1,108 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/enbility/eebus-go/service (interfaces: EEBUSServiceHandler) + +// Package service is a generated GoMock package. +package service + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockEEBUSServiceHandler is a mock of EEBUSServiceHandler interface. +type MockEEBUSServiceHandler struct { + ctrl *gomock.Controller + recorder *MockEEBUSServiceHandlerMockRecorder +} + +// MockEEBUSServiceHandlerMockRecorder is the mock recorder for MockEEBUSServiceHandler. +type MockEEBUSServiceHandlerMockRecorder struct { + mock *MockEEBUSServiceHandler +} + +// NewMockEEBUSServiceHandler creates a new mock instance. +func NewMockEEBUSServiceHandler(ctrl *gomock.Controller) *MockEEBUSServiceHandler { + mock := &MockEEBUSServiceHandler{ctrl: ctrl} + mock.recorder = &MockEEBUSServiceHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEEBUSServiceHandler) EXPECT() *MockEEBUSServiceHandlerMockRecorder { + return m.recorder +} + +// AllowWaitingForTrust mocks base method. +func (m *MockEEBUSServiceHandler) AllowWaitingForTrust(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AllowWaitingForTrust", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. +func (mr *MockEEBUSServiceHandlerMockRecorder) AllowWaitingForTrust(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).AllowWaitingForTrust), arg0) +} + +// RemoteSKIConnected mocks base method. +func (m *MockEEBUSServiceHandler) RemoteSKIConnected(arg0 *EEBUSService, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoteSKIConnected", arg0, arg1) +} + +// RemoteSKIConnected indicates an expected call of RemoteSKIConnected. +func (mr *MockEEBUSServiceHandlerMockRecorder) RemoteSKIConnected(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIConnected", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).RemoteSKIConnected), arg0, arg1) +} + +// RemoteSKIDisconnected mocks base method. +func (m *MockEEBUSServiceHandler) RemoteSKIDisconnected(arg0 *EEBUSService, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoteSKIDisconnected", arg0, arg1) +} + +// RemoteSKIDisconnected indicates an expected call of RemoteSKIDisconnected. +func (mr *MockEEBUSServiceHandlerMockRecorder) RemoteSKIDisconnected(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIDisconnected", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).RemoteSKIDisconnected), arg0, arg1) +} + +// ServicePairingDetailUpdate mocks base method. +func (m *MockEEBUSServiceHandler) ServicePairingDetailUpdate(arg0 string, arg1 *ConnectionStateDetail) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ServicePairingDetailUpdate", arg0, arg1) +} + +// ServicePairingDetailUpdate indicates an expected call of ServicePairingDetailUpdate. +func (mr *MockEEBUSServiceHandlerMockRecorder) ServicePairingDetailUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServicePairingDetailUpdate", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).ServicePairingDetailUpdate), arg0, arg1) +} + +// ServiceShipIDUpdate mocks base method. +func (m *MockEEBUSServiceHandler) ServiceShipIDUpdate(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ServiceShipIDUpdate", arg0, arg1) +} + +// ServiceShipIDUpdate indicates an expected call of ServiceShipIDUpdate. +func (mr *MockEEBUSServiceHandlerMockRecorder) ServiceShipIDUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceShipIDUpdate", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).ServiceShipIDUpdate), arg0, arg1) +} + +// VisibleRemoteServicesUpdated mocks base method. +func (m *MockEEBUSServiceHandler) VisibleRemoteServicesUpdated(arg0 *EEBUSService, arg1 []RemoteService) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "VisibleRemoteServicesUpdated", arg0, arg1) +} + +// VisibleRemoteServicesUpdated indicates an expected call of VisibleRemoteServicesUpdated. +func (mr *MockEEBUSServiceHandlerMockRecorder) VisibleRemoteServicesUpdated(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleRemoteServicesUpdated", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).VisibleRemoteServicesUpdated), arg0, arg1) +} diff --git a/service/service.go b/service/service.go index 15ea9359..299872c4 100644 --- a/service/service.go +++ b/service/service.go @@ -2,6 +2,7 @@ package service import ( "crypto/x509" + "errors" "fmt" "sync" @@ -19,6 +20,8 @@ type RemoteService struct { Model string `json:"model"` } +//go:generate mockgen -destination=mock_service_test.go -package=service github.com/enbility/eebus-go/service EEBUSServiceHandler + // interface for receiving data for specific events type EEBUSServiceHandler interface { // report all currently visible EEBUS services @@ -53,7 +56,7 @@ type EEBUSService struct { LocalService *ServiceDetails // Connection Registrations - connectionsHub *connectionsHub + connectionsHub ConnectionsHub // The SPINE specific device definition spineLocalDevice *spine.DeviceLocalImpl @@ -113,16 +116,16 @@ func (s *EEBUSService) ServicePairingDetailUpdate(ski string, detail *Connection s.serviceHandler.ServicePairingDetailUpdate(ski, detail) } -// Get the current pairing details for a given SKI -func (s *EEBUSService) PairingDetailForSki(ski string) *ConnectionStateDetail { - return s.connectionsHub.PairingDetailForSki(ski) -} - // return if the user is still able to trust the connection func (s *EEBUSService) AllowWaitingForTrust(ski string) bool { return s.serviceHandler.AllowWaitingForTrust(ski) } +// Get the current pairing details for a given SKI +func (s *EEBUSService) PairingDetailForSki(ski string) *ConnectionStateDetail { + return s.connectionsHub.PairingDetailForSki(ski) +} + // Starts browsing for any EEBUS mDNS entry func (s *EEBUSService) StartBrowseMdnsEntries() { s.connectionsHub.StartBrowseMdnsSearch() @@ -150,6 +153,10 @@ func (s *EEBUSService) Setup() error { sd := s.Configuration + if len(sd.certificate.Certificate) == 0 { + return errors.New("missing certificate") + } + leaf, err := x509.ParseCertificate(sd.certificate.Certificate[0]) if err != nil { return err @@ -220,14 +227,14 @@ func (s *EEBUSService) Setup() error { // Starts the service func (s *EEBUSService) Start() { s.startOnce.Do(func() { - s.connectionsHub.start() + s.connectionsHub.Start() }) } // Shutdown all services and stop the server. func (s *EEBUSService) Shutdown() { // Shut down all running connections - s.connectionsHub.shutdown() + s.connectionsHub.Shutdown() } func (s *EEBUSService) LocalDevice() *spine.DeviceLocalImpl { @@ -236,7 +243,7 @@ func (s *EEBUSService) LocalDevice() *spine.DeviceLocalImpl { // Returns the Service detail of a given remote SKI func (s *EEBUSService) RemoteServiceForSKI(ski string) *ServiceDetails { - return s.connectionsHub.serviceForSKI(ski) + return s.connectionsHub.ServiceForSKI(ski) } // Sets the SKI as being paired or not diff --git a/service/service_test.go b/service/service_test.go new file mode 100644 index 00000000..26de9761 --- /dev/null +++ b/service/service_test.go @@ -0,0 +1,147 @@ +package service + +import ( + "crypto/tls" + "testing" + "time" + + "github.com/enbility/eebus-go/logging" + "github.com/enbility/eebus-go/logging/mocks" + "github.com/enbility/eebus-go/spine/model" + gomock "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestServiceSuite(t *testing.T) { + suite.Run(t, new(ServiceSuite)) +} + +type ServiceSuite struct { + suite.Suite + + config *Configuration + + sut *EEBUSService + + serviceHandler *MockEEBUSServiceHandler + conHub *MockConnectionsHub + logging *mocks.Logging +} + +func (s *ServiceSuite) SetupSuite() {} +func (s *ServiceSuite) TearDownTest() {} + +func (s *ServiceSuite) BeforeTest(suiteName, testName string) { + ctrl := gomock.NewController(s.T()) + + s.serviceHandler = NewMockEEBUSServiceHandler(ctrl) + + s.conHub = NewMockConnectionsHub(ctrl) + + s.logging = mocks.NewLogging(s.T()) + s.logging.On("Info", mock.Anything, mock.Anything).Maybe() + + certificate := tls.Certificate{} + s.config, _ = NewConfiguration( + "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, 4729, certificate, 230.0, time.Second*4) + + s.sut = NewEEBUSService(s.config, s.serviceHandler) +} + +func (s *ServiceSuite) Test_EEBUSHandler() { + testSki := "test" + + entry := &MdnsEntry{ + Ski: testSki, + } + + entries := []*MdnsEntry{entry} + s.serviceHandler.EXPECT().VisibleRemoteServicesUpdated(gomock.Any(), gomock.Any()) + s.sut.VisibleMDNSRecordsUpdated(entries) + + s.serviceHandler.EXPECT().RemoteSKIConnected(gomock.Any(), gomock.Any()) + s.sut.RemoteSKIConnected(testSki) + + s.serviceHandler.EXPECT().RemoteSKIDisconnected(gomock.Any(), gomock.Any()) + s.sut.RemoteSKIDisconnected(testSki) + + s.serviceHandler.EXPECT().ServiceShipIDUpdate(gomock.Any(), gomock.Any()) + s.sut.ServiceShipIDUpdate(testSki, "shipid") + + s.serviceHandler.EXPECT().ServicePairingDetailUpdate(gomock.Any(), gomock.Any()) + detail := &ConnectionStateDetail{} + s.sut.ServicePairingDetailUpdate(testSki, detail) + + s.serviceHandler.EXPECT().AllowWaitingForTrust(gomock.Any()).Return(true) + result := s.sut.AllowWaitingForTrust(testSki) + assert.Equal(s.T(), true, result) + +} + +func (s *ServiceSuite) Test_ConnectionsHub() { + testSki := "test" + + s.sut.connectionsHub = s.conHub + + s.conHub.EXPECT().PairingDetailForSki(gomock.Any()) + s.sut.PairingDetailForSki(testSki) + + s.conHub.EXPECT().StartBrowseMdnsSearch() + s.sut.StartBrowseMdnsEntries() + + s.conHub.EXPECT().StopBrowseMdnsSearch() + s.sut.StopBrowseMdnsEntries() + + s.conHub.EXPECT().ServiceForSKI(gomock.Any()) + details := s.sut.RemoteServiceForSKI(testSki) + assert.Nil(s.T(), details) + + s.conHub.EXPECT().RegisterRemoteSKI(gomock.Any(), gomock.Any()) + s.sut.RegisterRemoteSKI(testSki, true) + + s.conHub.EXPECT().InitiatePairingWithSKI(gomock.Any()) + s.sut.InitiatePairingWithSKI(testSki) + + s.conHub.EXPECT().CancelPairingWithSKI(gomock.Any()) + s.sut.CancelPairingWithSKI(testSki) + + s.conHub.EXPECT().DisconnectSKI(gomock.Any(), gomock.Any()) + s.sut.DisconnectSKI(testSki, "reason") +} + +func (s *ServiceSuite) Test_SetLogging() { + s.sut.SetLogging(nil) + assert.Equal(s.T(), &logging.NoLogging{}, logging.Log) + + s.sut.SetLogging(s.logging) + assert.Equal(s.T(), s.logging, logging.Log) + + s.sut.SetLogging(nil) +} + +func (s *ServiceSuite) Test_Setup() { + + err := s.sut.Setup() + assert.NotNil(s.T(), err) + + s.config.certificate, err = CreateCertificate("unit", "org", "de", "cn") + assert.Nil(s.T(), err) + + err = s.sut.Setup() + assert.Nil(s.T(), err) + + s.sut.connectionsHub = s.conHub + s.conHub.EXPECT().Start() + s.sut.Start() + + time.Sleep(time.Millisecond * 200) + + s.conHub.EXPECT().Shutdown() + s.sut.Shutdown() + + device := s.sut.LocalDevice() + assert.NotNil(s.T(), device) +} From 55ef86a853c187d07fc14bace237dae998411a21 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Jan 2024 14:33:12 +0100 Subject: [PATCH 104/240] Add service types tests --- service/types_test.go | 125 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 service/types_test.go diff --git a/service/types_test.go b/service/types_test.go new file mode 100644 index 00000000..1259d538 --- /dev/null +++ b/service/types_test.go @@ -0,0 +1,125 @@ +package service + +import ( + "crypto/tls" + "errors" + "testing" + "time" + + spineModel "github.com/enbility/eebus-go/spine/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestTypesSuite(t *testing.T) { + suite.Run(t, new(TypesSuite)) +} + +type TypesSuite struct { + suite.Suite +} + +func (s *TypesSuite) SetupSuite() {} +func (s *TypesSuite) TearDownTest() {} + +func (s *TypesSuite) BeforeTest(suiteName, testName string) {} + +func (s *TypesSuite) Test_ConnectionState() { + conState := NewConnectionStateDetail(ConnectionStateNone, nil) + assert.Equal(s.T(), ConnectionStateNone, conState.State()) + assert.Nil(s.T(), conState.Error()) + + conState.SetState(ConnectionStateError) + assert.Equal(s.T(), ConnectionStateError, conState.State()) + + conState.SetError(errors.New("test")) + assert.NotNil(s.T(), conState.Error()) +} + +func (s *TypesSuite) Test_ServiceDetails() { + testSki := "test" + + details := NewServiceDetails(testSki) + assert.NotNil(s.T(), details) +} + +func (s *TypesSuite) Test_Configuration() { + certificate := tls.Certificate{} + vendor := "vendor" + brand := "brand" + model := "model" + serial := "serial" + port := 4567 + volt := 230.0 + + config, err := NewConfiguration("", brand, model, serial, spineModel.DeviceTypeTypeEnergyManagementSystem, + []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, volt, time.Second*4) + + assert.Nil(s.T(), config) + assert.NotNil(s.T(), err) + + config, err = NewConfiguration(vendor, "", model, serial, spineModel.DeviceTypeTypeEnergyManagementSystem, + []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + + assert.Nil(s.T(), config) + assert.NotNil(s.T(), err) + + config, err = NewConfiguration(vendor, brand, "", serial, spineModel.DeviceTypeTypeEnergyManagementSystem, + []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + + assert.Nil(s.T(), config) + assert.NotNil(s.T(), err) + + config, err = NewConfiguration(vendor, brand, model, "", spineModel.DeviceTypeTypeEnergyManagementSystem, + []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + + assert.Nil(s.T(), config) + assert.NotNil(s.T(), err) + + config, err = NewConfiguration(vendor, brand, model, serial, "", + []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + + assert.Nil(s.T(), config) + assert.NotNil(s.T(), err) + + config, err = NewConfiguration(vendor, brand, model, serial, spineModel.DeviceTypeTypeEnergyManagementSystem, + []spineModel.EntityTypeType{}, port, certificate, 230, time.Second*4) + + assert.Nil(s.T(), config) + assert.NotNil(s.T(), err) + + config, err = NewConfiguration(vendor, brand, model, serial, spineModel.DeviceTypeTypeEnergyManagementSystem, + []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + + assert.NotNil(s.T(), config) + assert.Nil(s.T(), err) + + ifaces := []string{"lo", "eth0"} + config.SetInterfaces(ifaces) + assert.Equal(s.T(), 2, len(config.interfaces)) + + config.SetRegisterAutoAccept(true) + assert.Equal(s.T(), true, config.registerAutoAccept) + + id := config.generateIdentifier() + assert.NotEqual(s.T(), "", id) + + id = config.Identifier() + assert.NotEqual(s.T(), "", id) + + id = config.MdnsServiceName() + assert.NotEqual(s.T(), "", id) + + alternate := "alternate" + + config.SetAlternateIdentifier(alternate) + id = config.Identifier() + assert.Equal(s.T(), alternate, id) + + config.SetAlternateMdnsServiceName(alternate) + id = config.MdnsServiceName() + assert.Equal(s.T(), alternate, id) + + voltage := config.Voltage() + assert.Equal(s.T(), volt, voltage) +} From b5a0c0ec009b3d793ea05fc8036f878475680358 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Jan 2024 14:53:04 +0100 Subject: [PATCH 105/240] Add more hub tests --- service/hub_test.go | 50 +++++++++++++++++++++++++++++++++++++++++ service/service_test.go | 1 + 2 files changed, 51 insertions(+) diff --git a/service/hub_test.go b/service/hub_test.go index 06b32818..b6bf2b97 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -223,6 +223,9 @@ func (s *HubSuite) Test_MapShipMessageExchangeState() { state = sut.mapShipMessageExchangeState(ship.SmeHelloStateAbort, ski) assert.Equal(s.T(), ConnectionStateNone, state) + state = sut.mapShipMessageExchangeState(ship.SmeHelloStateRemoteAbortDone, ski) + assert.Equal(s.T(), ConnectionStateRemoteDeniedTrust, state) + state = sut.mapShipMessageExchangeState(ship.SmePinStateCheckInit, ski) assert.Equal(s.T(), ConnectionStatePin, state) @@ -267,6 +270,15 @@ func (s *HubSuite) Test_RegisterConnection() { assert.NotNil(s.T(), con) } +func (s *HubSuite) Test_Shutdown() { + sut := connectionsHubImpl{ + connections: make(map[string]*ship.ShipConnection), + mdns: s.mdnsService, + } + s.mdnsService.EXPECT().ShutdownMdnsService() + sut.Shutdown() +} + func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { // we just need a dummy for this test @@ -396,3 +408,41 @@ func (s *HubSuite) Test_CancelPairingWithSKI() { sut.CancelPairingWithSKI(ski) assert.Equal(s.T(), 0, len(sut.connectionAttemptRunning)) } + +func (s *HubSuite) Test_ReportMdnsEntries() { + localService := &ServiceDetails{ + SKI: "localSKI", + } + sut := connectionsHubImpl{ + connections: make(map[string]*ship.ShipConnection), + connectionAttemptCounter: make(map[string]int), + connectionAttemptRunning: make(map[string]bool), + remoteServices: make(map[string]*ServiceDetails), + serviceProvider: s.serviceProvider, + localService: localService, + } + + testski1 := "test1" + testski2 := "test2" + + entries := make(map[string]*MdnsEntry) + + s.serviceProvider.EXPECT().VisibleMDNSRecordsUpdated(gomock.Any()).AnyTimes() + sut.ReportMdnsEntries(entries) + + entries[testski1] = &MdnsEntry{ + Ski: testski1, + } + service1 := sut.ServiceForSKI(testski1) + service1.Trusted = true + service1.IPv4 = "127.0.0.1" + + entries[testski2] = &MdnsEntry{ + Ski: testski2, + } + service2 := sut.ServiceForSKI(testski2) + service2.Trusted = true + service2.IPv4 = "127.0.0.1" + + sut.ReportMdnsEntries(entries) +} diff --git a/service/service_test.go b/service/service_test.go index 26de9761..72116499 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -42,6 +42,7 @@ func (s *ServiceSuite) BeforeTest(suiteName, testName string) { s.logging = mocks.NewLogging(s.T()) s.logging.On("Info", mock.Anything, mock.Anything).Maybe() + s.logging.On("Debug", mock.Anything, mock.Anything).Maybe() certificate := tls.Certificate{} s.config, _ = NewConfiguration( From cc421899a6e888544e143f98aded7bf02c23270d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 11:22:39 +0100 Subject: [PATCH 106/240] Add spine device remote tests --- spine/device_remote_test.go | 75 +++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 spine/device_remote_test.go diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go new file mode 100644 index 00000000..b20a0bbb --- /dev/null +++ b/spine/device_remote_test.go @@ -0,0 +1,75 @@ +package spine_test + +import ( + "testing" + "time" + + "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestDeviceRemoteSuite(t *testing.T) { + suite.Run(t, new(DeviceRemoteSuite)) +} + +type DeviceRemoteSuite struct { + suite.Suite + + localDevice *spine.DeviceLocalImpl + remoteDevice *spine.DeviceRemoteImpl +} + +func (s *DeviceRemoteSuite) WriteSpineMessage([]byte) {} + +func (s *DeviceRemoteSuite) SetupSuite() {} + +func (s *DeviceRemoteSuite) BeforeTest(suiteName, testName string) { + s.localDevice = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + + ski := "test" + sender := spine.NewSender(s) + s.remoteDevice = spine.NewDeviceRemoteImpl(s.localDevice, ski, sender) + s.localDevice.AddRemoteDevice(ski, s) + + entity := spine.NewEntityRemoteImpl(s.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + + feature := spine.NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + entity.AddFeature(feature) + + s.remoteDevice.AddEntity(entity) +} + +func (s *DeviceRemoteSuite) Test_RemoveByAddress() { + assert.Equal(s.T(), 2, len(s.remoteDevice.Entities())) + + s.remoteDevice.RemoveByAddress([]model.AddressEntityType{2}) + assert.Equal(s.T(), 2, len(s.remoteDevice.Entities())) + + s.remoteDevice.RemoveByAddress([]model.AddressEntityType{1}) + assert.Equal(s.T(), 1, len(s.remoteDevice.Entities())) +} + +func (s *DeviceRemoteSuite) Test_FeatureByEntityTypeAndRole() { + entity := s.remoteDevice.Entity([]model.AddressEntityType{1}) + assert.NotNil(s.T(), entity) + + assert.Equal(s.T(), 1, len(entity.Features())) + + feature := s.remoteDevice.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + assert.Nil(s.T(), feature) + + feature = s.remoteDevice.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + assert.NotNil(s.T(), feature) + + s.remoteDevice.RemoveByAddress([]model.AddressEntityType{1}) + assert.Equal(s.T(), 1, len(s.remoteDevice.Entities())) + + _ = s.remoteDevice.Entity([]model.AddressEntityType{0}) + s.remoteDevice.RemoveByAddress([]model.AddressEntityType{0}) + assert.Equal(s.T(), 0, len(s.remoteDevice.Entities())) + + feature = s.remoteDevice.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + assert.Nil(s.T(), feature) +} From 497fb1cf5889c1abec5319fdd00cdee77d58b4d8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 13:08:14 +0100 Subject: [PATCH 107/240] 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) +} From 36d099b9a186decfbb07be4086e8b8a772b116d2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 13:45:31 +0100 Subject: [PATCH 108/240] Fix tests and a bug in setlogging --- service/hub_test.go | 6 ++++++ service/mdns_test.go | 3 +-- service/service.go | 2 +- service/service_test.go | 6 ++---- service/types_test.go | 3 +-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/service/hub_test.go b/service/hub_test.go index b6bf2b97..ec6d9680 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -420,8 +420,14 @@ func (s *HubSuite) Test_ReportMdnsEntries() { remoteServices: make(map[string]*ServiceDetails), serviceProvider: s.serviceProvider, localService: localService, + mdns: s.mdnsService, } + certificate, _ := CreateCertificate("unit", "org", "DE", "CN") + sut.configuration, _ = NewConfiguration("vendor", "brand", "model", "serial", + model.DeviceTypeTypeGeneric, []model.EntityTypeType{model.EntityTypeTypeCEM}, + 4567, certificate, 230, time.Second*4) + testski1 := "test1" testski2 := "test2" diff --git a/service/mdns_test.go b/service/mdns_test.go index 6393f843..807d9c31 100644 --- a/service/mdns_test.go +++ b/service/mdns_test.go @@ -1,7 +1,6 @@ package service import ( - "crypto/tls" "net" "testing" "time" @@ -46,7 +45,7 @@ func (s *MdnsSuite) BeforeTest(suiteName, testName string) { s.mdnsProvider.On("ResolveEntries", mock.Anything, mock.Anything).Maybe().Return() s.mdnsProvider.On("Shutdown").Maybe().Return() - certificate := tls.Certificate{} + certificate, _ := CreateCertificate("unit", "org", "DE", "CN") s.config, _ = NewConfiguration( "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, diff --git a/service/service.go b/service/service.go index 299872c4..7ecab2a0 100644 --- a/service/service.go +++ b/service/service.go @@ -142,7 +142,7 @@ func (s *EEBUSService) SetLogging(logger logging.Logging) { if logger == nil { return } - logging.Log = logger + logging.SetLogging(logger) } // Starts the service by initializeing mDNS and the server. diff --git a/service/service_test.go b/service/service_test.go index 72116499..6318e133 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -10,7 +10,6 @@ import ( "github.com/enbility/eebus-go/spine/model" gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -41,8 +40,6 @@ func (s *ServiceSuite) BeforeTest(suiteName, testName string) { s.conHub = NewMockConnectionsHub(ctrl) s.logging = mocks.NewLogging(s.T()) - s.logging.On("Info", mock.Anything, mock.Anything).Maybe() - s.logging.On("Debug", mock.Anything, mock.Anything).Maybe() certificate := tls.Certificate{} s.config, _ = NewConfiguration( @@ -120,7 +117,8 @@ func (s *ServiceSuite) Test_SetLogging() { s.sut.SetLogging(s.logging) assert.Equal(s.T(), s.logging, logging.Log) - s.sut.SetLogging(nil) + s.sut.SetLogging(&logging.NoLogging{}) + assert.Equal(s.T(), &logging.NoLogging{}, logging.Log) } func (s *ServiceSuite) Test_Setup() { diff --git a/service/types_test.go b/service/types_test.go index 1259d538..28db095c 100644 --- a/service/types_test.go +++ b/service/types_test.go @@ -1,7 +1,6 @@ package service import ( - "crypto/tls" "errors" "testing" "time" @@ -44,7 +43,7 @@ func (s *TypesSuite) Test_ServiceDetails() { } func (s *TypesSuite) Test_Configuration() { - certificate := tls.Certificate{} + certificate, _ := CreateCertificate("unit", "org", "DE", "CN") vendor := "vendor" brand := "brand" model := "model" From 0141f27c806d7722a7ca683aa2f2eac59992fd90 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 16:24:10 +0100 Subject: [PATCH 109/240] Improve tests and fix a few bugs --- service/cert_test.go | 75 ++++++++- service/hub.go | 86 +++++----- service/hub_test.go | 377 +++++++++++++++++++++--------------------- service/types.go | 20 ++- service/types_test.go | 6 + ship/connection.go | 3 + 6 files changed, 336 insertions(+), 231 deletions(-) diff --git a/service/cert_test.go b/service/cert_test.go index 636b3be9..fc61bc31 100644 --- a/service/cert_test.go +++ b/service/cert_test.go @@ -1,8 +1,16 @@ package service import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha1" + "crypto/tls" "crypto/x509" + "crypto/x509/pkix" + "math/big" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -31,5 +39,70 @@ func (c *CertSuite) Test_SkiFromCertificate() { ski, err := skiFromCertificate(leaf) assert.Nil(c.T(), err) - assert.NotNil(c.T(), ski) + assert.NotEqual(c.T(), "", ski) + + cert, err = CreateInvalidCertificate("unit", "org", "DE", "CN") + assert.Nil(c.T(), err) + + leaf, err = x509.ParseCertificate(cert.Certificate[0]) + assert.Nil(c.T(), err) + + ski, err = skiFromCertificate(leaf) + assert.NotNil(c.T(), err) + assert.Equal(c.T(), "", ski) +} + +func CreateInvalidCertificate(organizationalUnit, organization, country, commonName string) (tls.Certificate, error) { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return tls.Certificate{}, err + } + + // Create the EEBUS service SKI using the private key + asn1, err := x509.MarshalECPrivateKey(privateKey) + if err != nil { + return tls.Certificate{}, err + } + // SHIP 12.2: Required to be created according to RFC 3280 4.2.1.2 + ski := sha1.Sum(asn1) + + subject := pkix.Name{ + OrganizationalUnit: []string{organizationalUnit}, + Organization: []string{organization}, + Country: []string{country}, + CommonName: commonName, + } + + // Create a random serial big int value + maxValue := new(big.Int) + maxValue.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxValue, big.NewInt(1)) + serialNumber, err := rand.Int(rand.Reader, maxValue) + if err != nil { + return tls.Certificate{}, err + } + + template := x509.Certificate{ + SignatureAlgorithm: x509.ECDSAWithSHA256, + SerialNumber: serialNumber, + Subject: subject, + NotBefore: time.Now(), // Valid starting now + NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10), // Valid for 10 years + KeyUsage: x509.KeyUsageDigitalSignature, + BasicConstraintsValid: true, + IsCA: true, + SubjectKeyId: ski[:19], + } + + certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) + if err != nil { + return tls.Certificate{}, err + } + + tlsCertificate := tls.Certificate{ + Certificate: [][]byte{certBytes}, + PrivateKey: privateKey, + SupportedSignatureAlgorithms: []tls.SignatureScheme{tls.ECDSAWithP256AndSHA256}, + } + + return tlsCertificate, nil } diff --git a/service/hub.go b/service/hub.go index 2b2a73ce..98873024 100644 --- a/service/hub.go +++ b/service/hub.go @@ -103,7 +103,7 @@ type connectionsHubImpl struct { knownMdnsEntries []*MdnsEntry // the SPINE local device - spineLocalDevice *spine.DeviceLocalImpl + spineLocalDevice spine.DeviceLocalConnection muxCon sync.Mutex muxConAttempt sync.Mutex @@ -111,7 +111,7 @@ type connectionsHubImpl struct { muxMdns sync.Mutex } -func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice *spine.DeviceLocalImpl, configuration *Configuration, localService *ServiceDetails) ConnectionsHub { +func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice spine.DeviceLocalConnection, configuration *Configuration, localService *ServiceDetails) ConnectionsHub { hub := &connectionsHubImpl{ connections: make(map[string]*ship.ShipConnection), connectionAttemptCounter: make(map[string]int), @@ -257,9 +257,9 @@ func (h *connectionsHubImpl) HandleShipHandshakeStateUpdate(ski string, state sh service := h.ServiceForSKI(ski) - existingDetails := service.ConnectionStateDetail + existingDetails := service.ConnectionStateDetail() if existingDetails.State() != pairingState || existingDetails.Error() != state.Error { - service.ConnectionStateDetail = pairingDetail + service.SetConnectionStateDetail(pairingDetail) h.serviceProvider.ServicePairingDetailUpdate(ski, pairingDetail) } @@ -280,7 +280,7 @@ func (h *connectionsHubImpl) PairingDetailForSki(ski string) *ConnectionStateDet return NewConnectionStateDetail(state, shipError) } - return service.ConnectionStateDetail + return service.ConnectionStateDetail() } // maps ShipMessageExchangeState to PairingState @@ -375,6 +375,25 @@ func (h *connectionsHubImpl) isSkiConnected(ski string) bool { } // Websocket connection handling +func (h *connectionsHubImpl) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + skiFound := false + for _, v := range rawCerts { + cert, err := x509.ParseCertificate(v) + if err != nil { + return err + } + + if _, err := skiFromCertificate(cert); err == nil { + skiFound = true + break + } + } + if !skiFound { + return errors.New("no valid SKI provided in certificate") + } + + return nil +} // start the ship websocket server func (h *connectionsHubImpl) startWebsocketServer() error { @@ -385,28 +404,10 @@ func (h *connectionsHubImpl) startWebsocketServer() error { Addr: addr, Handler: h, TLSConfig: &tls.Config{ - Certificates: []tls.Certificate{h.configuration.certificate}, - ClientAuth: tls.RequireAnyClientCert, // SHIP 9: Client authentication is required - CipherSuites: ciperSuites, - VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - skiFound := false - for _, v := range rawCerts { - cert, err := x509.ParseCertificate(v) - if err != nil { - return err - } - - if _, err := skiFromCertificate(cert); err == nil { - skiFound = true - break - } - } - if !skiFound { - return errors.New("no valid SKI provided in certificate") - } - - return nil - }, + Certificates: []tls.Certificate{h.configuration.certificate}, + ClientAuth: tls.RequireAnyClientCert, // SHIP 9: Client authentication is required + CipherSuites: ciperSuites, + VerifyPeerCertificate: h.verifyPeerCertificate, }, } @@ -445,7 +446,7 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // check if the clients certificate provides a SKI - if len(r.TLS.PeerCertificates) == 0 { + if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 { logging.Log.Debug("client does not provide a certificate") _ = conn.Close() return @@ -464,9 +465,10 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Check if the remote service is paired service := h.ServiceForSKI(remoteService.SKI) - if service.ConnectionStateDetail.State() == ConnectionStateQueued { - service.ConnectionStateDetail.SetState(ConnectionStateReceivedPairingRequest) - h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) + connectionStateDetail := service.ConnectionStateDetail() + if connectionStateDetail.State() == ConnectionStateQueued { + connectionStateDetail.SetState(ConnectionStateReceivedPairingRequest) + h.serviceProvider.ServicePairingDetailUpdate(ski, connectionStateDetail) } remoteService = service @@ -608,7 +610,7 @@ func (h *connectionsHubImpl) ServiceForSKI(ski string) *ServiceDetails { service, ok := h.remoteServices[ski] if !ok { service = NewServiceDetails(ski) - service.ConnectionStateDetail.SetState(ConnectionStateNone) + service.ConnectionStateDetail().SetState(ConnectionStateNone) h.remoteServices[ski] = service } @@ -629,9 +631,9 @@ func (h *connectionsHubImpl) RegisterRemoteSKI(ski string, enable bool) { h.removeConnectionAttemptCounter(ski) - service.ConnectionStateDetail.SetState(ConnectionStateNone) + service.ConnectionStateDetail().SetState(ConnectionStateNone) - h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) + h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) if existingC := h.connectionForSKI(ski); existingC != nil { existingC.CloseConnection(true, 4500, "User close") @@ -651,9 +653,9 @@ func (h *connectionsHubImpl) InitiatePairingWithSKI(ski string) { // locally initiated service := h.ServiceForSKI(ski) - service.ConnectionStateDetail.SetState(ConnectionStateQueued) + service.ConnectionStateDetail().SetState(ConnectionStateQueued) - h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) + h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) // initiate a search and also a connection if it does not yet exist if !h.isSkiConnected(service.SKI) { @@ -670,10 +672,10 @@ func (h *connectionsHubImpl) CancelPairingWithSKI(ski string) { } service := h.ServiceForSKI(ski) - service.ConnectionStateDetail.SetState(ConnectionStateNone) + service.ConnectionStateDetail().SetState(ConnectionStateNone) service.Trusted = false - h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail) + h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) } // Process reported mDNS services @@ -694,7 +696,7 @@ func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*MdnsEntry) { // Check if the remote service is paired or queued for connection service := h.ServiceForSKI(ski) if !h.IsRemoteServiceForSKIPaired(ski) && - service.ConnectionStateDetail.State() != ConnectionStateQueued { + service.ConnectionStateDetail().State() != ConnectionStateQueued { continue } @@ -732,7 +734,7 @@ func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *M counter, duration := h.getConnectionInitiationDelayTime(ski) service := h.ServiceForSKI(ski) - if service.ConnectionStateDetail.State() == ConnectionStateQueued { + if service.ConnectionStateDetail().State() == ConnectionStateQueued { go h.prepareConnectionInitation(ski, counter, entry) return } @@ -762,7 +764,7 @@ func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing - pairingState := h.ServiceForSKI(ski).ConnectionStateDetail.State() + pairingState := h.ServiceForSKI(ski).ConnectionStateDetail().State() if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != ConnectionStateQueued { return } @@ -790,7 +792,7 @@ func (h *connectionsHubImpl) initateConnection(remoteService *ServiceDetails, en for _, address := range entry.Addresses { // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing - pairingState := h.ServiceForSKI(remoteService.SKI).ConnectionStateDetail.State() + pairingState := h.ServiceForSKI(remoteService.SKI).ConnectionStateDetail().State() if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != ConnectionStateQueued { return false } diff --git a/service/hub_test.go b/service/hub_test.go index ec6d9680..1ff44960 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -1,13 +1,18 @@ package service import ( + "crypto/tls" "errors" + "net/http" + "net/http/httptest" + "strings" "testing" "time" "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine/model" gomock "github.com/golang/mock/gomock" + "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -28,6 +33,8 @@ type HubSuite struct { mdnsService *MockMdnsService tests []testStruct + + sut *connectionsHubImpl } func (s *HubSuite) SetupSuite() { @@ -62,6 +69,27 @@ func (s *HubSuite) SetupSuite() { s.mdnsService.EXPECT().UnregisterMdnsSearch(gomock.Any()).AnyTimes() } +func (s *HubSuite) BeforeTest(suiteName, testName string) { + localService := &ServiceDetails{ + SKI: "localSKI", + } + + s.sut = &connectionsHubImpl{ + connections: make(map[string]*ship.ShipConnection), + connectionAttemptCounter: make(map[string]int), + connectionAttemptRunning: make(map[string]bool), + remoteServices: make(map[string]*ServiceDetails), + serviceProvider: s.serviceProvider, + localService: localService, + mdns: s.mdnsService, + } + + certificate, _ := CreateCertificate("unit", "org", "DE", "CN") + s.sut.configuration, _ = NewConfiguration("vendor", "brand", "model", "serial", + model.DeviceTypeTypeGeneric, []model.EntityTypeType{model.EntityTypeTypeCEM}, + 4567, certificate, 230, time.Second*4) +} + func (s *HubSuite) Test_NewConnectionsHub() { ski := "12af9e" localService := NewServiceDetails(ski) @@ -76,224 +104,251 @@ func (s *HubSuite) Test_NewConnectionsHub() { } func (s *HubSuite) Test_IsRemoteSKIPaired() { - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - connectionAttemptCounter: make(map[string]int), - remoteServices: make(map[string]*ServiceDetails), - serviceProvider: s.serviceProvider, - } ski := "test" - paired := sut.IsRemoteServiceForSKIPaired(ski) + paired := s.sut.IsRemoteServiceForSKIPaired(ski) assert.Equal(s.T(), false, paired) // mark it as connected, so mDNS is not triggered con := &ship.ShipConnection{ RemoteSKI: ski, } - sut.registerConnection(con) - sut.RegisterRemoteSKI(ski, true) + s.sut.registerConnection(con) + s.sut.RegisterRemoteSKI(ski, true) - paired = sut.IsRemoteServiceForSKIPaired(ski) + paired = s.sut.IsRemoteServiceForSKIPaired(ski) assert.Equal(s.T(), true, paired) // remove the connection, so the test doesn't try to close it - delete(sut.connections, ski) - sut.RegisterRemoteSKI(ski, false) - paired = sut.IsRemoteServiceForSKIPaired(ski) + delete(s.sut.connections, ski) + s.sut.RegisterRemoteSKI(ski, false) + paired = s.sut.IsRemoteServiceForSKIPaired(ski) assert.Equal(s.T(), false, paired) } func (s *HubSuite) Test_HandleConnecitonClosed() { - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - connectionAttemptCounter: make(map[string]int), - remoteServices: make(map[string]*ServiceDetails), - serviceProvider: s.serviceProvider, - } ski := "test" con := &ship.ShipConnection{ RemoteSKI: ski, } - sut.HandleConnectionClosed(con, false) + s.sut.HandleConnectionClosed(con, false) - sut.registerConnection(con) + s.sut.registerConnection(con) - sut.HandleConnectionClosed(con, true) + s.sut.HandleConnectionClosed(con, true) - assert.Equal(s.T(), 0, len(sut.connections)) + assert.Equal(s.T(), 0, len(s.sut.connections)) } func (s *HubSuite) Test_Mdns() { - localService := ServiceDetails{ - DeviceType: model.DeviceTypeTypeElectricitySupplySystem, - } - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - connectionAttemptCounter: make(map[string]int), - remoteServices: make(map[string]*ServiceDetails), - localService: &localService, - mdns: s.mdnsService, - serviceProvider: s.serviceProvider, - } - sut.checkRestartMdnsSearch() + s.sut.checkRestartMdnsSearch() - pairedServices := sut.numberPairedServices() - assert.Equal(s.T(), 0, len(sut.connections)) + pairedServices := s.sut.numberPairedServices() + assert.Equal(s.T(), 0, len(s.sut.connections)) assert.Equal(s.T(), 0, pairedServices) ski := "testski" - sut.RegisterRemoteSKI(ski, true) - pairedServices = sut.numberPairedServices() - assert.Equal(s.T(), 0, len(sut.connections)) + s.sut.RegisterRemoteSKI(ski, true) + pairedServices = s.sut.numberPairedServices() + assert.Equal(s.T(), 0, len(s.sut.connections)) assert.Equal(s.T(), 1, pairedServices) - sut.StartBrowseMdnsSearch() + s.sut.StartBrowseMdnsSearch() - sut.StopBrowseMdnsSearch() + s.sut.StopBrowseMdnsSearch() } func (s *HubSuite) Test_Ship() { - localService := ServiceDetails{ - DeviceType: model.DeviceTypeTypeElectricitySupplySystem, - } - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - connectionAttemptCounter: make(map[string]int), - remoteServices: make(map[string]*ServiceDetails), - localService: &localService, - mdns: s.mdnsService, - serviceProvider: s.serviceProvider, - } - ski := "testski" - sut.HandleShipHandshakeStateUpdate(ski, ship.ShipState{ + s.sut.HandleShipHandshakeStateUpdate(ski, ship.ShipState{ State: ship.SmeStateError, Error: errors.New("test"), }) - sut.HandleShipHandshakeStateUpdate(ski, ship.ShipState{ + s.sut.HandleShipHandshakeStateUpdate(ski, ship.ShipState{ State: ship.SmeHelloStateOk, }) - sut.ReportServiceShipID(ski, "test") + s.sut.ReportServiceShipID(ski, "test") - trust := sut.AllowWaitingForTrust(ski) + trust := s.sut.AllowWaitingForTrust(ski) assert.Equal(s.T(), true, trust) - trust = sut.AllowWaitingForTrust("test") + trust = s.sut.AllowWaitingForTrust("test") assert.Equal(s.T(), false, trust) - detail := sut.PairingDetailForSki(ski) + detail := s.sut.PairingDetailForSki(ski) assert.NotNil(s.T(), detail) con := &ship.ShipConnection{ RemoteSKI: ski, } - sut.registerConnection(con) + s.sut.registerConnection(con) - detail = sut.PairingDetailForSki(ski) + detail = s.sut.PairingDetailForSki(ski) assert.NotNil(s.T(), detail) } func (s *HubSuite) Test_MapShipMessageExchangeState() { - sut := connectionsHubImpl{} - ski := "test" - state := sut.mapShipMessageExchangeState(ship.CmiStateInitStart, ski) + state := s.sut.mapShipMessageExchangeState(ship.CmiStateInitStart, ski) assert.Equal(s.T(), ConnectionStateQueued, state) - state = sut.mapShipMessageExchangeState(ship.CmiStateClientSend, ski) + state = s.sut.mapShipMessageExchangeState(ship.CmiStateClientSend, ski) assert.Equal(s.T(), ConnectionStateInitiated, state) - state = sut.mapShipMessageExchangeState(ship.SmeHelloStateReadyInit, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateReadyInit, ski) assert.Equal(s.T(), ConnectionStateInProgress, state) - state = sut.mapShipMessageExchangeState(ship.SmeHelloStatePendingInit, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStatePendingInit, ski) assert.Equal(s.T(), ConnectionStateReceivedPairingRequest, state) - state = sut.mapShipMessageExchangeState(ship.SmeHelloStateOk, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateOk, ski) assert.Equal(s.T(), ConnectionStateTrusted, state) - state = sut.mapShipMessageExchangeState(ship.SmeHelloStateAbort, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateAbort, ski) assert.Equal(s.T(), ConnectionStateNone, state) - state = sut.mapShipMessageExchangeState(ship.SmeHelloStateRemoteAbortDone, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateRemoteAbortDone, ski) assert.Equal(s.T(), ConnectionStateRemoteDeniedTrust, state) - state = sut.mapShipMessageExchangeState(ship.SmePinStateCheckInit, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmePinStateCheckInit, ski) assert.Equal(s.T(), ConnectionStatePin, state) - state = sut.mapShipMessageExchangeState(ship.SmeAccessMethodsRequest, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeAccessMethodsRequest, ski) assert.Equal(s.T(), ConnectionStateInProgress, state) - state = sut.mapShipMessageExchangeState(ship.SmeStateComplete, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeStateComplete, ski) assert.Equal(s.T(), ConnectionStateCompleted, state) - state = sut.mapShipMessageExchangeState(ship.SmeStateError, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeStateError, ski) assert.Equal(s.T(), ConnectionStateError, state) - state = sut.mapShipMessageExchangeState(ship.SmeProtHStateTimeout, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeProtHStateTimeout, ski) assert.Equal(s.T(), ConnectionStateInProgress, state) } func (s *HubSuite) Test_DisconnectSKI() { - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - } ski := "test" - sut.DisconnectSKI(ski, "none") + s.sut.DisconnectSKI(ski, "none") } func (s *HubSuite) Test_RegisterConnection() { - ski := "12af9e" - localService := NewServiceDetails(ski) - - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - mdns: s.mdnsService, - localService: localService, - } - - ski = "test" + ski := "test" con := &ship.ShipConnection{ RemoteSKI: ski, } - sut.registerConnection(con) - assert.Equal(s.T(), 1, len(sut.connections)) - con = sut.connectionForSKI(ski) + s.sut.registerConnection(con) + assert.Equal(s.T(), 1, len(s.sut.connections)) + con = s.sut.connectionForSKI(ski) assert.NotNil(s.T(), con) } func (s *HubSuite) Test_Shutdown() { - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - mdns: s.mdnsService, - } s.mdnsService.EXPECT().ShutdownMdnsService() - sut.Shutdown() + s.sut.Shutdown() } -func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { +func (s *HubSuite) Test_VerifyPeerCertificate() { + testCert, _ := CreateCertificate("unit", "org", "DE", "CN") + var rawCerts [][]byte + rawCerts = append(rawCerts, testCert.Certificate...) + err := s.sut.verifyPeerCertificate(rawCerts, nil) + assert.Nil(s.T(), err) - // we just need a dummy for this test - sut := connectionsHubImpl{ - connectionAttemptCounter: make(map[string]int), + rawCerts = nil + rawCerts = append(rawCerts, []byte{100}) + err = s.sut.verifyPeerCertificate(rawCerts, nil) + assert.NotNil(s.T(), err) + + rawCerts = nil + invalidCert, _ := CreateInvalidCertificate("unit", "org", "DE", "CN") + rawCerts = append(rawCerts, invalidCert.Certificate...) + + err = s.sut.verifyPeerCertificate(rawCerts, nil) + assert.NotNil(s.T(), err) +} + +func (s *HubSuite) Test_ServeHTTP() { + req := httptest.NewRequest("GET", "http://example.com/foo", nil) + w := httptest.NewRecorder() + s.sut.ServeHTTP(w, req) + + server := httptest.NewServer(s.sut) + wsURL := strings.Replace(server.URL, "http://", "ws://", -1) + + // Connect to the server + con, _, err := websocket.DefaultDialer.Dial(wsURL, nil) + assert.Nil(s.T(), err) + con.Close() + + dialer := &websocket.Dialer{ + Subprotocols: []string{shipWebsocketSubProtocol}, } + con, _, err = dialer.Dial(wsURL, nil) + assert.Nil(s.T(), err) + con.Close() + server.Close() + + server = httptest.NewUnstartedServer(s.sut) + server.TLS = &tls.Config{ + Certificates: []tls.Certificate{s.sut.configuration.certificate}, + ClientAuth: tls.RequireAnyClientCert, + CipherSuites: ciperSuites, + InsecureSkipVerify: true, + } + server.StartTLS() + wsURL = strings.Replace(server.URL, "https://", "wss://", -1) + + invalidCert, _ := CreateInvalidCertificate("unit", "org", "DE", "CN") + dialer = &websocket.Dialer{ + Proxy: http.ProxyFromEnvironment, + HandshakeTimeout: 5 * time.Second, + TLSClientConfig: &tls.Config{ + Certificates: []tls.Certificate{invalidCert}, + InsecureSkipVerify: true, + CipherSuites: ciperSuites, + }, + Subprotocols: []string{shipWebsocketSubProtocol}, + } + con, _, err = dialer.Dial(wsURL, nil) + assert.Nil(s.T(), err) + + con.Close() + + validCert, _ := CreateCertificate("unit", "org", "DE", "CN") + dialer = &websocket.Dialer{ + Proxy: http.ProxyFromEnvironment, + HandshakeTimeout: 5 * time.Second, + TLSClientConfig: &tls.Config{ + Certificates: []tls.Certificate{validCert}, + InsecureSkipVerify: true, + CipherSuites: ciperSuites, + }, + Subprotocols: []string{shipWebsocketSubProtocol}, + } + con, _, err = dialer.Dial(wsURL, nil) + assert.Nil(s.T(), err) + + con.Close() + server.Close() +} + +func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { ski := "test" for _, test := range s.tests { - sut.increaseConnectionAttemptCounter(ski) + s.sut.increaseConnectionAttemptCounter(ski) - sut.muxConAttempt.Lock() - counter, exists := sut.connectionAttemptCounter[ski] + s.sut.muxConAttempt.Lock() + counter, exists := s.sut.connectionAttemptCounter[ski] timeRange := connectionInitiationDelayTimeRanges[counter] - sut.muxConAttempt.Unlock() + s.sut.muxConAttempt.Unlock() assert.Equal(s.T(), true, exists) assert.Equal(s.T(), test.timeRange.min, timeRange.min) @@ -302,153 +357,103 @@ func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { } func (s *HubSuite) Test_RemoveConnectionAttemptCounter() { - // we just need a dummy for this test - sut := connectionsHubImpl{ - connectionAttemptCounter: make(map[string]int), - } ski := "test" - sut.increaseConnectionAttemptCounter(ski) - _, exists := sut.connectionAttemptCounter[ski] + s.sut.increaseConnectionAttemptCounter(ski) + _, exists := s.sut.connectionAttemptCounter[ski] assert.Equal(s.T(), true, exists) - sut.removeConnectionAttemptCounter(ski) - _, exists = sut.connectionAttemptCounter[ski] + s.sut.removeConnectionAttemptCounter(ski) + _, exists = s.sut.connectionAttemptCounter[ski] assert.Equal(s.T(), false, exists) } func (s *HubSuite) Test_GetCurrentConnectionAttemptCounter() { - // we just need a dummy for this test - sut := connectionsHubImpl{ - connectionAttemptCounter: make(map[string]int), - } ski := "test" - sut.increaseConnectionAttemptCounter(ski) - _, exists := sut.connectionAttemptCounter[ski] + s.sut.increaseConnectionAttemptCounter(ski) + _, exists := s.sut.connectionAttemptCounter[ski] assert.Equal(s.T(), exists, true) - sut.increaseConnectionAttemptCounter(ski) + s.sut.increaseConnectionAttemptCounter(ski) - value, exists := sut.getCurrentConnectionAttemptCounter(ski) + value, exists := s.sut.getCurrentConnectionAttemptCounter(ski) assert.Equal(s.T(), 1, value) assert.Equal(s.T(), true, exists) } func (s *HubSuite) Test_GetConnectionInitiationDelayTime() { - // we just need a dummy for this test - ski := "12af9e" - localService := NewServiceDetails(ski) - sut := connectionsHubImpl{ - localService: localService, - connectionAttemptCounter: make(map[string]int), - } + ski := "test" - counter, duration := sut.getConnectionInitiationDelayTime(ski) + counter, duration := s.sut.getConnectionInitiationDelayTime(ski) assert.Equal(s.T(), 0, counter) assert.LessOrEqual(s.T(), float64(s.tests[counter].timeRange.min), float64(duration/time.Second)) assert.GreaterOrEqual(s.T(), float64(s.tests[counter].timeRange.max), float64(duration/time.Second)) } func (s *HubSuite) Test_ConnectionAttemptRunning() { - // we just need a dummy for this test ski := "test" - sut := connectionsHubImpl{ - connectionAttemptRunning: make(map[string]bool), - } - sut.setConnectionAttemptRunning(ski, true) - status := sut.isConnectionAttemptRunning(ski) + s.sut.setConnectionAttemptRunning(ski, true) + status := s.sut.isConnectionAttemptRunning(ski) assert.Equal(s.T(), true, status) - sut.setConnectionAttemptRunning(ski, false) - status = sut.isConnectionAttemptRunning(ski) + s.sut.setConnectionAttemptRunning(ski, false) + status = s.sut.isConnectionAttemptRunning(ski) assert.Equal(s.T(), false, status) } func (s *HubSuite) Test_InitiatePairingWithSKI() { ski := "test" - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - connectionAttemptRunning: make(map[string]bool), - remoteServices: make(map[string]*ServiceDetails), - serviceProvider: s.serviceProvider, - mdns: s.mdnsService, - } - sut.InitiatePairingWithSKI(ski) - assert.Equal(s.T(), 0, len(sut.connections)) + s.sut.InitiatePairingWithSKI(ski) + assert.Equal(s.T(), 0, len(s.sut.connections)) con := &ship.ShipConnection{ RemoteSKI: ski, } - sut.registerConnection(con) - sut.InitiatePairingWithSKI(ski) - assert.Equal(s.T(), 1, len(sut.connections)) + s.sut.registerConnection(con) + s.sut.InitiatePairingWithSKI(ski) + assert.Equal(s.T(), 1, len(s.sut.connections)) } func (s *HubSuite) Test_CancelPairingWithSKI() { ski := "test" - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - connectionAttemptRunning: make(map[string]bool), - remoteServices: make(map[string]*ServiceDetails), - serviceProvider: s.serviceProvider, - mdns: s.mdnsService, - } - sut.CancelPairingWithSKI(ski) - assert.Equal(s.T(), 0, len(sut.connections)) - assert.Equal(s.T(), 0, len(sut.connectionAttemptRunning)) + s.sut.CancelPairingWithSKI(ski) + assert.Equal(s.T(), 0, len(s.sut.connections)) + assert.Equal(s.T(), 0, len(s.sut.connectionAttemptRunning)) con := &ship.ShipConnection{ RemoteSKI: ski, } - sut.registerConnection(con) - assert.Equal(s.T(), 1, len(sut.connections)) + s.sut.registerConnection(con) + assert.Equal(s.T(), 1, len(s.sut.connections)) - sut.CancelPairingWithSKI(ski) - assert.Equal(s.T(), 0, len(sut.connectionAttemptRunning)) + s.sut.CancelPairingWithSKI(ski) + assert.Equal(s.T(), 0, len(s.sut.connectionAttemptRunning)) } func (s *HubSuite) Test_ReportMdnsEntries() { - localService := &ServiceDetails{ - SKI: "localSKI", - } - sut := connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), - connectionAttemptCounter: make(map[string]int), - connectionAttemptRunning: make(map[string]bool), - remoteServices: make(map[string]*ServiceDetails), - serviceProvider: s.serviceProvider, - localService: localService, - mdns: s.mdnsService, - } - - certificate, _ := CreateCertificate("unit", "org", "DE", "CN") - sut.configuration, _ = NewConfiguration("vendor", "brand", "model", "serial", - model.DeviceTypeTypeGeneric, []model.EntityTypeType{model.EntityTypeTypeCEM}, - 4567, certificate, 230, time.Second*4) - testski1 := "test1" testski2 := "test2" entries := make(map[string]*MdnsEntry) s.serviceProvider.EXPECT().VisibleMDNSRecordsUpdated(gomock.Any()).AnyTimes() - sut.ReportMdnsEntries(entries) + s.sut.ReportMdnsEntries(entries) entries[testski1] = &MdnsEntry{ Ski: testski1, } - service1 := sut.ServiceForSKI(testski1) + service1 := s.sut.ServiceForSKI(testski1) service1.Trusted = true service1.IPv4 = "127.0.0.1" entries[testski2] = &MdnsEntry{ Ski: testski2, } - service2 := sut.ServiceForSKI(testski2) + service2 := s.sut.ServiceForSKI(testski2) service2.Trusted = true service2.IPv4 = "127.0.0.1" - sut.ReportMdnsEntries(entries) + s.sut.ReportMdnsEntries(entries) } diff --git a/service/types.go b/service/types.go index aa537f54..4b5b4a83 100644 --- a/service/types.go +++ b/service/types.go @@ -101,7 +101,9 @@ type ServiceDetails struct { Trusted bool // the current connection state details - ConnectionStateDetail *ConnectionStateDetail + connectionStateDetail *ConnectionStateDetail + + mux sync.Mutex } // create a new ServiceDetails record with a SKI @@ -109,12 +111,26 @@ func NewServiceDetails(ski string) *ServiceDetails { connState := NewConnectionStateDetail(ConnectionStateNone, nil) service := &ServiceDetails{ SKI: util.NormalizeSKI(ski), // standardize the provided SKI strings - ConnectionStateDetail: connState, + connectionStateDetail: connState, } return service } +func (s *ServiceDetails) ConnectionStateDetail() *ConnectionStateDetail { + s.mux.Lock() + defer s.mux.Unlock() + + return s.connectionStateDetail +} + +func (s *ServiceDetails) SetConnectionStateDetail(detail *ConnectionStateDetail) { + s.mux.Lock() + defer s.mux.Unlock() + + s.connectionStateDetail = detail +} + // defines requires meta information about this service type Configuration struct { // The vendors IANA PEN, optional but highly recommended. diff --git a/service/types_test.go b/service/types_test.go index 28db095c..29ee5de7 100644 --- a/service/types_test.go +++ b/service/types_test.go @@ -40,6 +40,12 @@ func (s *TypesSuite) Test_ServiceDetails() { details := NewServiceDetails(testSki) assert.NotNil(s.T(), details) + + conState := NewConnectionStateDetail(ConnectionStateNone, nil) + details.SetConnectionStateDetail(conState) + + state := details.ConnectionStateDetail() + assert.Equal(s.T(), ConnectionStateNone, state.State()) } func (s *TypesSuite) Test_Configuration() { diff --git a/ship/connection.go b/ship/connection.go index 583aecd7..6434943e 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -132,6 +132,9 @@ func (c *ShipConnection) AbortPendingHandshake() { // report removing a connection func (c *ShipConnection) removeRemoteDeviceConnection() { + if c.deviceLocalCon == nil { + return + } c.deviceLocalCon.RemoveRemoteDeviceConnection(c.RemoteSKI) } From 1e072cec22c702fa9816f3aa41037d2fe34dc25f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 16:33:00 +0100 Subject: [PATCH 110/240] Add a small test --- ship/connection_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ship/connection_test.go b/ship/connection_test.go index 97b68b89..8966b426 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -88,6 +88,14 @@ func (s *ConnectionSuite) TestAbortPendingHandshake() { assert.Equal(s.T(), SmeHelloStateAbortDone, s.sut.smeState) } +func (s *ConnectionSuite) TestRemoveRemoteDeviceConnection() { + s.sut.removeRemoteDeviceConnection() + + s.sut.deviceLocalCon = nil + + s.sut.removeRemoteDeviceConnection() +} + func (s *ConnectionSuite) TestCloseConnection_StateComplete() { s.sut.smeState = SmeStateComplete s.sut.CloseConnection(true, 450, "User Close") From b2c15e1651ffdd38395b8e013a03a585d16df4da Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 18:52:25 +0100 Subject: [PATCH 111/240] Refactor ShipConnection and add tests --- service/hub.go | 36 ++-- service/hub_test.go | 292 ++++++++++++++++++++----------- ship/connection.go | 82 +++++---- ship/connection_test.go | 2 +- ship/handshake.go | 38 ++-- ship/hs_access.go | 6 +- ship/hs_hello.go | 18 +- ship/hs_helper_test.go | 6 +- ship/hs_init.go | 12 +- ship/hs_pin.go | 4 +- ship/hs_prot.go | 14 +- ship/mock_types_test.go | 2 +- ship/mocks/ShipConnection.go | 108 ++++++++++++ ship/mocks/ShipDataConnection.go | 83 +++++++++ ship/types.go | 13 +- 15 files changed, 509 insertions(+), 207 deletions(-) create mode 100644 ship/mocks/ShipConnection.go create mode 100644 ship/mocks/ShipDataConnection.go diff --git a/service/hub.go b/service/hub.go index 98873024..bf4d61bf 100644 --- a/service/hub.go +++ b/service/hub.go @@ -79,7 +79,7 @@ type ConnectionsHub interface { // handling all connections to remote services type connectionsHubImpl struct { - connections map[string]*ship.ShipConnection + connections map[string]ship.ShipConnection // which attempt is it to initate an connection to the remote SKI connectionAttemptCounter map[string]int @@ -113,7 +113,7 @@ type connectionsHubImpl struct { func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice spine.DeviceLocalConnection, configuration *Configuration, localService *ServiceDetails) ConnectionsHub { hub := &connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), + connections: make(map[string]ship.ShipConnection), connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), remoteServices: make(map[string]*ServiceDetails), @@ -154,26 +154,26 @@ func (h *connectionsHubImpl) IsRemoteServiceForSKIPaired(ski string) bool { } // The connection was closed, we need to clean up -func (h *connectionsHubImpl) HandleConnectionClosed(connection *ship.ShipConnection, handshakeCompleted bool) { +func (h *connectionsHubImpl) HandleConnectionClosed(connection ship.ShipConnection, handshakeCompleted bool) { // only remove this connection if it is the registered one for the ski! // as we can have double connections but only one can be registered - if existingC := h.connectionForSKI(connection.RemoteSKI); existingC != nil { - if existingC.DataHandler == connection.DataHandler { + if existingC := h.connectionForSKI(connection.RemoteSKI()); existingC != nil { + if existingC.DataHandler() == connection.DataHandler() { h.muxCon.Lock() - delete(h.connections, connection.RemoteSKI) + delete(h.connections, connection.RemoteSKI()) h.muxCon.Unlock() } // connection close was after a completed handshake, so we can reset the attetmpt counter if handshakeCompleted { - h.removeConnectionAttemptCounter(connection.RemoteSKI) + h.removeConnectionAttemptCounter(connection.RemoteSKI()) } } - h.serviceProvider.RemoteSKIDisconnected(connection.RemoteSKI) + h.serviceProvider.RemoteSKIDisconnected(connection.RemoteSKI()) // Do not automatically reconnect if handshake failed and not already paired - remoteService := h.ServiceForSKI(connection.RemoteSKI) + remoteService := h.ServiceForSKI(connection.RemoteSKI()) if !handshakeCompleted && !remoteService.Trusted { return } @@ -338,14 +338,14 @@ func (h *connectionsHubImpl) DisconnectSKI(ski string, reason string) { } // register a new ship Connection -func (h *connectionsHubImpl) registerConnection(connection *ship.ShipConnection) { +func (h *connectionsHubImpl) registerConnection(connection ship.ShipConnection) { h.muxCon.Lock() - h.connections[connection.RemoteSKI] = connection + h.connections[connection.RemoteSKI()] = connection h.muxCon.Unlock() } // return the connection for a specific SKI -func (h *connectionsHubImpl) connectionForSKI(ski string) *ship.ShipConnection { +func (h *connectionsHubImpl) connectionForSKI(ski string) ship.ShipConnection { h.muxCon.Lock() defer h.muxCon.Unlock() @@ -592,11 +592,13 @@ func (h *connectionsHubImpl) keepThisConnection(conn *websocket.Conn, incomingRe connType = "outgoing" } logging.Log.Debugf("closing %s double connection, as the existing connection will be used", connType) - go func() { - _ = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "double connection")) - time.Sleep(time.Millisecond * 100) - conn.Close() - }() + if conn != nil { + go func() { + _ = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "double connection")) + time.Sleep(time.Millisecond * 100) + conn.Close() + }() + } } return keep diff --git a/service/hub_test.go b/service/hub_test.go index 1ff44960..e516ad58 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -3,17 +3,21 @@ package service import ( "crypto/tls" "errors" + "net" "net/http" "net/http/httptest" + "net/url" "strings" "testing" "time" "github.com/enbility/eebus-go/ship" + "github.com/enbility/eebus-go/ship/mocks" "github.com/enbility/eebus-go/spine/model" gomock "github.com/golang/mock/gomock" "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -29,8 +33,12 @@ type testStruct struct { type HubSuite struct { suite.Suite - serviceProvider *MockServiceProvider - mdnsService *MockMdnsService + serviceProvider *MockServiceProvider + mdnsService *MockMdnsService + shipConnection *mocks.ShipConnection + shipDataConnection *mocks.ShipDataConnection + + remoteSki string tests []testStruct @@ -38,6 +46,8 @@ type HubSuite struct { } func (s *HubSuite) SetupSuite() { + s.remoteSki = "remotetestski" + s.tests = []testStruct{ {0, connectionInitiationDelayTimeRanges[0]}, {1, connectionInitiationDelayTimeRanges[1]}, @@ -67,6 +77,16 @@ func (s *HubSuite) SetupSuite() { s.mdnsService.EXPECT().UnannounceMdnsEntry().AnyTimes() s.mdnsService.EXPECT().RegisterMdnsSearch(gomock.Any()).AnyTimes() s.mdnsService.EXPECT().UnregisterMdnsSearch(gomock.Any()).AnyTimes() + + s.shipDataConnection = mocks.NewShipDataConnection(s.T()) + + s.shipConnection = mocks.NewShipConnection(s.T()) + s.shipConnection.On("CloseConnection", mock.Anything, mock.Anything, mock.Anything).Return().Maybe() + s.shipConnection.On("RemoteSKI").Return(s.remoteSki).Maybe() + s.shipConnection.On("ApprovePendingHandshake").Return().Maybe() + s.shipConnection.On("AbortPendingHandshake").Return().Maybe() + s.shipConnection.On("DataHandler").Return(s.shipDataConnection).Maybe() + s.shipConnection.On("ShipHandshakeState").Return(ship.SmeStateComplete, nil).Maybe() } func (s *HubSuite) BeforeTest(suiteName, testName string) { @@ -75,7 +95,7 @@ func (s *HubSuite) BeforeTest(suiteName, testName string) { } s.sut = &connectionsHubImpl{ - connections: make(map[string]*ship.ShipConnection), + connections: make(map[string]ship.ShipConnection), connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), remoteServices: make(map[string]*ServiceDetails), @@ -104,40 +124,28 @@ func (s *HubSuite) Test_NewConnectionsHub() { } func (s *HubSuite) Test_IsRemoteSKIPaired() { - ski := "test" - - paired := s.sut.IsRemoteServiceForSKIPaired(ski) + paired := s.sut.IsRemoteServiceForSKIPaired(s.remoteSki) assert.Equal(s.T(), false, paired) - // mark it as connected, so mDNS is not triggered - con := &ship.ShipConnection{ - RemoteSKI: ski, - } - s.sut.registerConnection(con) - s.sut.RegisterRemoteSKI(ski, true) + s.sut.registerConnection(s.shipConnection) + s.sut.RegisterRemoteSKI(s.remoteSki, true) - paired = s.sut.IsRemoteServiceForSKIPaired(ski) + paired = s.sut.IsRemoteServiceForSKIPaired(s.remoteSki) assert.Equal(s.T(), true, paired) // remove the connection, so the test doesn't try to close it - delete(s.sut.connections, ski) - s.sut.RegisterRemoteSKI(ski, false) - paired = s.sut.IsRemoteServiceForSKIPaired(ski) + delete(s.sut.connections, s.remoteSki) + s.sut.RegisterRemoteSKI(s.remoteSki, false) + paired = s.sut.IsRemoteServiceForSKIPaired(s.remoteSki) assert.Equal(s.T(), false, paired) } func (s *HubSuite) Test_HandleConnecitonClosed() { - ski := "test" + s.sut.HandleConnectionClosed(s.shipConnection, false) - con := &ship.ShipConnection{ - RemoteSKI: ski, - } - - s.sut.HandleConnectionClosed(con, false) - - s.sut.registerConnection(con) + s.sut.registerConnection(s.shipConnection) - s.sut.HandleConnectionClosed(con, true) + s.sut.HandleConnectionClosed(s.shipConnection, true) assert.Equal(s.T(), 0, len(s.sut.connections)) } @@ -149,9 +157,7 @@ func (s *HubSuite) Test_Mdns() { assert.Equal(s.T(), 0, len(s.sut.connections)) assert.Equal(s.T(), 0, pairedServices) - ski := "testski" - - s.sut.RegisterRemoteSKI(ski, true) + s.sut.RegisterRemoteSKI(s.remoteSki, true) pairedServices = s.sut.numberPairedServices() assert.Equal(s.T(), 0, len(s.sut.connections)) assert.Equal(s.T(), 1, pairedServices) @@ -162,90 +168,78 @@ func (s *HubSuite) Test_Mdns() { } func (s *HubSuite) Test_Ship() { - ski := "testski" - - s.sut.HandleShipHandshakeStateUpdate(ski, ship.ShipState{ + s.sut.HandleShipHandshakeStateUpdate(s.remoteSki, ship.ShipState{ State: ship.SmeStateError, Error: errors.New("test"), }) - s.sut.HandleShipHandshakeStateUpdate(ski, ship.ShipState{ + s.sut.HandleShipHandshakeStateUpdate(s.remoteSki, ship.ShipState{ State: ship.SmeHelloStateOk, }) - s.sut.ReportServiceShipID(ski, "test") + s.sut.ReportServiceShipID(s.remoteSki, "test") - trust := s.sut.AllowWaitingForTrust(ski) + trust := s.sut.AllowWaitingForTrust(s.remoteSki) assert.Equal(s.T(), true, trust) trust = s.sut.AllowWaitingForTrust("test") assert.Equal(s.T(), false, trust) - detail := s.sut.PairingDetailForSki(ski) + detail := s.sut.PairingDetailForSki(s.remoteSki) assert.NotNil(s.T(), detail) - con := &ship.ShipConnection{ - RemoteSKI: ski, - } - s.sut.registerConnection(con) + s.sut.registerConnection(s.shipConnection) - detail = s.sut.PairingDetailForSki(ski) + detail = s.sut.PairingDetailForSki(s.remoteSki) assert.NotNil(s.T(), detail) } func (s *HubSuite) Test_MapShipMessageExchangeState() { - ski := "test" - - state := s.sut.mapShipMessageExchangeState(ship.CmiStateInitStart, ski) + state := s.sut.mapShipMessageExchangeState(ship.CmiStateInitStart, s.remoteSki) assert.Equal(s.T(), ConnectionStateQueued, state) - state = s.sut.mapShipMessageExchangeState(ship.CmiStateClientSend, ski) + state = s.sut.mapShipMessageExchangeState(ship.CmiStateClientSend, s.remoteSki) assert.Equal(s.T(), ConnectionStateInitiated, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateReadyInit, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateReadyInit, s.remoteSki) assert.Equal(s.T(), ConnectionStateInProgress, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStatePendingInit, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStatePendingInit, s.remoteSki) assert.Equal(s.T(), ConnectionStateReceivedPairingRequest, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateOk, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateOk, s.remoteSki) assert.Equal(s.T(), ConnectionStateTrusted, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateAbort, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateAbort, s.remoteSki) assert.Equal(s.T(), ConnectionStateNone, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateRemoteAbortDone, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateRemoteAbortDone, s.remoteSki) assert.Equal(s.T(), ConnectionStateRemoteDeniedTrust, state) - state = s.sut.mapShipMessageExchangeState(ship.SmePinStateCheckInit, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmePinStateCheckInit, s.remoteSki) assert.Equal(s.T(), ConnectionStatePin, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeAccessMethodsRequest, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeAccessMethodsRequest, s.remoteSki) assert.Equal(s.T(), ConnectionStateInProgress, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeStateComplete, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeStateComplete, s.remoteSki) assert.Equal(s.T(), ConnectionStateCompleted, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeStateError, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeStateError, s.remoteSki) assert.Equal(s.T(), ConnectionStateError, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeProtHStateTimeout, ski) + state = s.sut.mapShipMessageExchangeState(ship.SmeProtHStateTimeout, s.remoteSki) assert.Equal(s.T(), ConnectionStateInProgress, state) } func (s *HubSuite) Test_DisconnectSKI() { - ski := "test" - s.sut.DisconnectSKI(ski, "none") + s.sut.DisconnectSKI(s.remoteSki, "none") } func (s *HubSuite) Test_RegisterConnection() { - ski := "test" - con := &ship.ShipConnection{ - RemoteSKI: ski, - } - s.sut.registerConnection(con) + s.sut.registerConnection(s.shipConnection) assert.Equal(s.T(), 1, len(s.sut.connections)) - con = s.sut.connectionForSKI(ski) + con := s.sut.connectionForSKI(s.remoteSki) assert.NotNil(s.T(), con) } @@ -339,14 +333,124 @@ func (s *HubSuite) Test_ServeHTTP() { server.Close() } -func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { - ski := "test" +func (s *HubSuite) Test_ConnectFoundService() { + service := s.sut.ServiceForSKI(s.remoteSki) + err := s.sut.connectFoundService(service, "localhost", "80") + assert.NotNil(s.T(), err) + + server := httptest.NewServer(s.sut) + url, err := url.Parse(server.URL) + assert.Nil(s.T(), err) + + err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) + assert.NotNil(s.T(), err) + + server.Close() + + server = httptest.NewUnstartedServer(s.sut) + invalidCert, _ := CreateInvalidCertificate("unit", "org", "DE", "CN") + server.TLS = &tls.Config{ + Certificates: []tls.Certificate{invalidCert}, + ClientAuth: tls.RequireAnyClientCert, + CipherSuites: ciperSuites, + InsecureSkipVerify: true, + } + server.StartTLS() + + url, err = url.Parse(server.URL) + assert.Nil(s.T(), err) + + err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) + assert.NotNil(s.T(), err) + + server.Close() + + server = httptest.NewUnstartedServer(s.sut) + server.TLS = &tls.Config{ + Certificates: []tls.Certificate{s.sut.configuration.certificate}, + ClientAuth: tls.RequireAnyClientCert, + CipherSuites: ciperSuites, + InsecureSkipVerify: true, + } + server.StartTLS() + + url, err = url.Parse(server.URL) + assert.Nil(s.T(), err) + + err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) + assert.NotNil(s.T(), err) + + server.Close() +} + +func (s *HubSuite) Test_KeepThisConnection() { + service := s.sut.ServiceForSKI(s.remoteSki) + + result := s.sut.keepThisConnection(nil, false, service) + assert.Equal(s.T(), true, result) + + s.sut.registerConnection(s.shipConnection) + + result = s.sut.keepThisConnection(nil, false, service) + assert.Equal(s.T(), false, result) + + result = s.sut.keepThisConnection(nil, true, service) + assert.Equal(s.T(), true, result) +} + +func (s *HubSuite) Test_prepareConnectionInitiation() { + entry := &MdnsEntry{ + Ski: s.remoteSki, + Host: "somehost", + } + service := s.sut.ServiceForSKI(s.remoteSki) + + s.sut.prepareConnectionInitation(s.remoteSki, 0, entry) + + s.sut.setConnectionAttemptRunning(s.remoteSki, true) + + counter := s.sut.increaseConnectionAttemptCounter(s.remoteSki) + assert.Equal(s.T(), 0, counter) + s.sut.prepareConnectionInitation(s.remoteSki, 0, entry) + + s.sut.RegisterRemoteSKI(s.remoteSki, false) + service.ConnectionStateDetail().SetState(ConnectionStateQueued) + + counter = s.sut.increaseConnectionAttemptCounter(s.remoteSki) + assert.Equal(s.T(), 0, counter) + + s.sut.prepareConnectionInitation(s.remoteSki, 0, entry) +} + +func (s *HubSuite) Test_InitiateConnection() { + entry := &MdnsEntry{ + Ski: s.remoteSki, + Host: "somehost", + } + service := s.sut.ServiceForSKI(s.remoteSki) + + result := s.sut.initateConnection(service, entry) + assert.Equal(s.T(), false, result) + + entry.Addresses = []net.IP{[]byte("127.0.0.1")} + + result = s.sut.initateConnection(service, entry) + assert.Equal(s.T(), false, result) + + s.sut.RegisterRemoteSKI(s.remoteSki, true) + service.ConnectionStateDetail().SetState(ConnectionStateQueued) + + result = s.sut.initateConnection(service, entry) + assert.Equal(s.T(), false, result) +} + +func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { for _, test := range s.tests { - s.sut.increaseConnectionAttemptCounter(ski) + s.sut.increaseConnectionAttemptCounter(s.remoteSki) s.sut.muxConAttempt.Lock() - counter, exists := s.sut.connectionAttemptCounter[ski] + counter, exists := s.sut.connectionAttemptCounter[s.remoteSki] timeRange := connectionInitiationDelayTimeRanges[counter] s.sut.muxConAttempt.Unlock() @@ -357,78 +461,60 @@ func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { } func (s *HubSuite) Test_RemoveConnectionAttemptCounter() { - ski := "test" - - s.sut.increaseConnectionAttemptCounter(ski) - _, exists := s.sut.connectionAttemptCounter[ski] + s.sut.increaseConnectionAttemptCounter(s.remoteSki) + _, exists := s.sut.connectionAttemptCounter[s.remoteSki] assert.Equal(s.T(), true, exists) - s.sut.removeConnectionAttemptCounter(ski) - _, exists = s.sut.connectionAttemptCounter[ski] + s.sut.removeConnectionAttemptCounter(s.remoteSki) + _, exists = s.sut.connectionAttemptCounter[s.remoteSki] assert.Equal(s.T(), false, exists) } func (s *HubSuite) Test_GetCurrentConnectionAttemptCounter() { - ski := "test" - - s.sut.increaseConnectionAttemptCounter(ski) - _, exists := s.sut.connectionAttemptCounter[ski] + s.sut.increaseConnectionAttemptCounter(s.remoteSki) + _, exists := s.sut.connectionAttemptCounter[s.remoteSki] assert.Equal(s.T(), exists, true) - s.sut.increaseConnectionAttemptCounter(ski) + s.sut.increaseConnectionAttemptCounter(s.remoteSki) - value, exists := s.sut.getCurrentConnectionAttemptCounter(ski) + value, exists := s.sut.getCurrentConnectionAttemptCounter(s.remoteSki) assert.Equal(s.T(), 1, value) assert.Equal(s.T(), true, exists) } func (s *HubSuite) Test_GetConnectionInitiationDelayTime() { - ski := "test" - - counter, duration := s.sut.getConnectionInitiationDelayTime(ski) + counter, duration := s.sut.getConnectionInitiationDelayTime(s.remoteSki) assert.Equal(s.T(), 0, counter) assert.LessOrEqual(s.T(), float64(s.tests[counter].timeRange.min), float64(duration/time.Second)) assert.GreaterOrEqual(s.T(), float64(s.tests[counter].timeRange.max), float64(duration/time.Second)) } func (s *HubSuite) Test_ConnectionAttemptRunning() { - ski := "test" - - s.sut.setConnectionAttemptRunning(ski, true) - status := s.sut.isConnectionAttemptRunning(ski) + s.sut.setConnectionAttemptRunning(s.remoteSki, true) + status := s.sut.isConnectionAttemptRunning(s.remoteSki) assert.Equal(s.T(), true, status) - s.sut.setConnectionAttemptRunning(ski, false) - status = s.sut.isConnectionAttemptRunning(ski) + s.sut.setConnectionAttemptRunning(s.remoteSki, false) + status = s.sut.isConnectionAttemptRunning(s.remoteSki) assert.Equal(s.T(), false, status) } func (s *HubSuite) Test_InitiatePairingWithSKI() { - ski := "test" - - s.sut.InitiatePairingWithSKI(ski) + s.sut.InitiatePairingWithSKI(s.remoteSki) assert.Equal(s.T(), 0, len(s.sut.connections)) - con := &ship.ShipConnection{ - RemoteSKI: ski, - } - s.sut.registerConnection(con) - s.sut.InitiatePairingWithSKI(ski) + s.sut.registerConnection(s.shipConnection) + s.sut.InitiatePairingWithSKI(s.remoteSki) assert.Equal(s.T(), 1, len(s.sut.connections)) } func (s *HubSuite) Test_CancelPairingWithSKI() { - ski := "test" - - s.sut.CancelPairingWithSKI(ski) + s.sut.CancelPairingWithSKI(s.remoteSki) assert.Equal(s.T(), 0, len(s.sut.connections)) assert.Equal(s.T(), 0, len(s.sut.connectionAttemptRunning)) - con := &ship.ShipConnection{ - RemoteSKI: ski, - } - s.sut.registerConnection(con) + s.sut.registerConnection(s.shipConnection) assert.Equal(s.T(), 1, len(s.sut.connections)) - s.sut.CancelPairingWithSKI(ski) + s.sut.CancelPairingWithSKI(s.remoteSki) assert.Equal(s.T(), 0, len(s.sut.connectionAttemptRunning)) } diff --git a/ship/connection.go b/ship/connection.go index 6434943e..b629c54c 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -15,13 +15,13 @@ import ( "github.com/enbility/eebus-go/util" ) -// A ShipConnection handles the data connection and coordinates SHIP and SPINE messages i/o -type ShipConnection struct { +// A ShipConnectionImpl handles the data connection and coordinates SHIP and SPINE messages i/o +type ShipConnectionImpl struct { // The ship connection mode of this connection role shipRole // The remote SKI - RemoteSKI string + remoteSKI string // the remote SHIP Id remoteShipID string @@ -36,7 +36,7 @@ type ShipConnection struct { spineDataProcessing spine.SpineDataProcessing // the handler for sending messages on the data connection - DataHandler ShipDataConnection + dataHandler ShipDataConnection // The current SHIP state smeState ShipMessageExchangeState @@ -66,38 +66,50 @@ type ShipConnection struct { mux sync.Mutex } -func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler ShipDataConnection, deviceLocalCon spine.DeviceLocalConnection, role shipRole, localShipID, remoteSki, remoteShipId string) *ShipConnection { - ship := &ShipConnection{ +var _ ShipConnection = (*ShipConnectionImpl)(nil) + +func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler ShipDataConnection, deviceLocalCon spine.DeviceLocalConnection, role shipRole, localShipID, remoteSki, remoteShipId string) *ShipConnectionImpl { + ship := &ShipConnectionImpl{ serviceDataProvider: dataProvider, deviceLocalCon: deviceLocalCon, role: role, localShipID: localShipID, - RemoteSKI: remoteSki, + remoteSKI: remoteSki, remoteShipID: remoteShipId, - DataHandler: dataHandler, + dataHandler: dataHandler, smeState: CmiStateInitStart, smeError: nil, } ship.handshakeTimerStopChan = make(chan struct{}) - dataHandler.InitDataProcessing(ship) + if dataHandler != nil { + dataHandler.InitDataProcessing(ship) + } return ship } +func (c *ShipConnectionImpl) RemoteSKI() string { + return c.remoteSKI +} + +func (c *ShipConnectionImpl) DataHandler() ShipDataConnection { + return c.dataHandler +} + // start SHIP communication -func (c *ShipConnection) Run() { +func (c *ShipConnectionImpl) Run() { c.handleShipMessage(false, nil) } // provides the current ship state and error value if the state is in error -func (c *ShipConnection) ShipHandshakeState() (ShipMessageExchangeState, error) { +func (c *ShipConnectionImpl) ShipHandshakeState() (ShipMessageExchangeState, error) { return c.getState(), c.smeError } // invoked when pairing for a pending request is approved -func (c *ShipConnection) ApprovePendingHandshake() { +func (c *ShipConnectionImpl) ApprovePendingHandshake() { state := c.getState() if state != SmeHelloStatePendingListen { // TODO: what to do if the state is different? @@ -116,7 +128,7 @@ func (c *ShipConnection) ApprovePendingHandshake() { } // invoked when pairing for a pending request is denied -func (c *ShipConnection) AbortPendingHandshake() { +func (c *ShipConnectionImpl) AbortPendingHandshake() { state := c.getState() if state != SmeHelloStatePendingListen && state != SmeHelloStateReadyListen { // TODO: what to do if the state is differnet? @@ -131,15 +143,15 @@ func (c *ShipConnection) AbortPendingHandshake() { } // report removing a connection -func (c *ShipConnection) removeRemoteDeviceConnection() { +func (c *ShipConnectionImpl) removeRemoteDeviceConnection() { if c.deviceLocalCon == nil { return } - c.deviceLocalCon.RemoveRemoteDeviceConnection(c.RemoteSKI) + c.deviceLocalCon.RemoveRemoteDeviceConnection(c.remoteSKI) } // close this ship connection -func (c *ShipConnection) CloseConnection(safe bool, code int, reason string) { +func (c *ShipConnectionImpl) CloseConnection(safe bool, code int, reason string) { c.shutdownOnce.Do(func() { c.stopHandshakeTimer() @@ -174,25 +186,25 @@ func (c *ShipConnection) CloseConnection(safe bool, code int, reason string) { if code != 0 { closeCode = code } - c.DataHandler.CloseDataConnection(closeCode, reason) + c.dataHandler.CloseDataConnection(closeCode, reason) c.serviceDataProvider.HandleConnectionClosed(c, handshakeEnd) }) } -var _ spine.SpineDataConnection = (*ShipConnection)(nil) +var _ spine.SpineDataConnection = (*ShipConnectionImpl)(nil) // SpineDataConnection interface implementation -func (c *ShipConnection) WriteSpineMessage(message []byte) { +func (c *ShipConnectionImpl) WriteSpineMessage(message []byte) { if err := c.sendSpineData(message); err != nil { logging.Log.Debug(c.RemoteSKI, "Error sending spine message: ", err) return } } -var _ ShipDataProcessing = (*ShipConnection)(nil) +var _ ShipDataProcessing = (*ShipConnectionImpl)(nil) -func (c *ShipConnection) shipModelFromMessage(message []byte) (*model.ShipData, error) { +func (c *ShipConnectionImpl) shipModelFromMessage(message []byte) (*model.ShipData, error) { _, jsonData := c.parseMessage(message, true) // Get the datagram from the message @@ -212,7 +224,7 @@ func (c *ShipConnection) shipModelFromMessage(message []byte) (*model.ShipData, } // route the incoming message to either SHIP or SPINE message handlers -func (c *ShipConnection) HandleIncomingShipMessage(message []byte) { +func (c *ShipConnectionImpl) HandleIncomingShipMessage(message []byte) { // Check if this is a SHIP SME or SPINE message if !c.hasSpineDatagram(message) { c.handleShipMessage(false, message) @@ -233,12 +245,12 @@ func (c *ShipConnection) HandleIncomingShipMessage(message []byte) { } // checks wether the provided messages is a SHIP message -func (c *ShipConnection) hasSpineDatagram(message []byte) bool { +func (c *ShipConnectionImpl) hasSpineDatagram(message []byte) bool { return bytes.Contains(message, []byte("datagram")) } // the websocket data connection was closed from remote -func (c *ShipConnection) ReportConnectionError(err error) { +func (c *ShipConnectionImpl) ReportConnectionError(err error) { // if the handshake is aborted, a closed connection is no error currentState := c.getState() @@ -270,12 +282,12 @@ func (c *ShipConnection) ReportConnectionError(err error) { State: SmeStateError, Error: err, } - c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.RemoteSKI, state) + c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.remoteSKI, state) } const payloadPlaceholder = `{"place":"holder"}` -func (c *ShipConnection) transformSpineDataIntoShipJson(data []byte) ([]byte, error) { +func (c *ShipConnectionImpl) transformSpineDataIntoShipJson(data []byte) ([]byte, error) { spineMsg, err := shipUtil.JsonIntoEEBUSJson(data) if err != nil { return nil, err @@ -313,13 +325,13 @@ func (c *ShipConnection) transformSpineDataIntoShipJson(data []byte) ([]byte, er return []byte(eebusMsg), nil } -func (c *ShipConnection) sendSpineData(data []byte) error { +func (c *ShipConnectionImpl) sendSpineData(data []byte) error { eebusMsg, err := c.transformSpineDataIntoShipJson(data) if err != nil { return err } - if isClosed, err := c.DataHandler.IsDataConnectionClosed(); isClosed { + if isClosed, err := c.dataHandler.IsDataConnectionClosed(); isClosed { c.CloseConnection(false, 0, "") return err } @@ -328,7 +340,7 @@ func (c *ShipConnection) sendSpineData(data []byte) error { shipMsg := []byte{model.MsgTypeData} shipMsg = append(shipMsg, eebusMsg...) - err = c.DataHandler.WriteMessageToDataConnection(shipMsg) + err = c.dataHandler.WriteMessageToDataConnection(shipMsg) if err != nil { logging.Log.Debug("error sending message: ", err) return err @@ -338,13 +350,13 @@ func (c *ShipConnection) sendSpineData(data []byte) error { } // send a json message for a provided model to the websocket connection -func (c *ShipConnection) sendShipModel(typ byte, model interface{}) error { +func (c *ShipConnectionImpl) sendShipModel(typ byte, model interface{}) error { shipMsg, err := c.shipMessage(typ, model) if err != nil { return err } - err = c.DataHandler.WriteMessageToDataConnection(shipMsg) + err = c.dataHandler.WriteMessageToDataConnection(shipMsg) if err != nil { return err } @@ -353,15 +365,15 @@ func (c *ShipConnection) sendShipModel(typ byte, model interface{}) error { } // Process a SHIP Json message -func (c *ShipConnection) processShipJsonMessage(message []byte, target any) error { +func (c *ShipConnectionImpl) processShipJsonMessage(message []byte, target any) error { _, data := c.parseMessage(message, true) return json.Unmarshal(data, &target) } // transform a SHIP model into EEBUS specific JSON -func (c *ShipConnection) shipMessage(typ byte, model interface{}) ([]byte, error) { - if isClosed, err := c.DataHandler.IsDataConnectionClosed(); isClosed { +func (c *ShipConnectionImpl) shipMessage(typ byte, model interface{}) ([]byte, error) { + if isClosed, err := c.dataHandler.IsDataConnectionClosed(); isClosed { c.CloseConnection(false, 0, "") return nil, err } @@ -390,7 +402,7 @@ func (c *ShipConnection) shipMessage(typ byte, model interface{}) ([]byte, error // return the SHIP message type, the SHIP message and an error // // enable jsonFormat if the return message is expected to be encoded in the eebus json format -func (c *ShipConnection) parseMessage(msg []byte, jsonFormat bool) (byte, []byte) { +func (c *ShipConnectionImpl) parseMessage(msg []byte, jsonFormat bool) (byte, []byte) { if len(msg) == 0 { return 0, nil } diff --git a/ship/connection_test.go b/ship/connection_test.go index 8966b426..0587bae2 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -22,7 +22,7 @@ func TestConnectionSuite(t *testing.T) { type ConnectionSuite struct { suite.Suite - sut *ShipConnection + sut *ShipConnectionImpl shipDataProvider *MockShipServiceDataProvider shipDataConn *MockShipDataConnection diff --git a/ship/handshake.go b/ship/handshake.go index fe119b45..26889ca8 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -9,7 +9,7 @@ import ( ) // handle incoming SHIP messages and coordinate Handshake States -func (c *ShipConnection) handleShipMessage(timeout bool, message []byte) { +func (c *ShipConnectionImpl) handleShipMessage(timeout bool, message []byte) { if len(message) > 2 { var closeMsg model.ConnectionClose err := c.processShipJsonMessage(message, &closeMsg) @@ -29,11 +29,11 @@ func (c *ShipConnection) handleShipMessage(timeout bool, message []byte) { <-time.After(500 * time.Millisecond) // - c.DataHandler.CloseDataConnection(4001, "close") + c.dataHandler.CloseDataConnection(4001, "close") c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeStateComplete) case model.ConnectionClosePhaseTypeConfirm: // we got a confirmation so close this connection - c.DataHandler.CloseDataConnection(4001, "close") + c.dataHandler.CloseDataConnection(4001, "close") c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeStateComplete) } @@ -45,7 +45,7 @@ func (c *ShipConnection) handleShipMessage(timeout bool, message []byte) { } // set a new handshake state and handle timers if needed -func (c *ShipConnection) setState(newState ShipMessageExchangeState, err error) { +func (c *ShipConnectionImpl) setState(newState ShipMessageExchangeState, err error) { c.mux.Lock() oldState := c.smeState @@ -75,13 +75,13 @@ func (c *ShipConnection) setState(newState ShipMessageExchangeState, err error) Error: err, } c.mux.Unlock() - c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.RemoteSKI, state) + c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.remoteSKI, state) return } c.mux.Unlock() } -func (c *ShipConnection) getState() ShipMessageExchangeState { +func (c *ShipConnectionImpl) getState() ShipMessageExchangeState { c.mux.Lock() defer c.mux.Unlock() @@ -89,7 +89,7 @@ func (c *ShipConnection) getState() ShipMessageExchangeState { } // handle handshake state transitions -func (c *ShipConnection) handleState(timeout bool, message []byte) { +func (c *ShipConnectionImpl) handleState(timeout bool, message []byte) { switch c.getState() { case SmeStateError: logging.Log.Debug(c.RemoteSKI, "connection is in error state") @@ -123,7 +123,7 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { // pairing service // go to substate ready if so, otherwise to substate pending - if c.serviceDataProvider.IsRemoteServiceForSKIPaired(c.RemoteSKI) || c.role == ShipRoleClient { + if c.serviceDataProvider.IsRemoteServiceForSKIPaired(c.remoteSKI) || c.role == ShipRoleClient { c.setState(SmeHelloStateReadyInit, nil) } else { c.setState(SmeHelloStatePendingInit, nil) @@ -191,21 +191,21 @@ func (c *ShipConnection) handleState(timeout bool, message []byte) { } // set a state and trigger handling it -func (c *ShipConnection) setAndHandleState(state ShipMessageExchangeState) { +func (c *ShipConnectionImpl) setAndHandleState(state ShipMessageExchangeState) { c.setState(state, nil) c.handleState(false, nil) } // SHIP handshake is approved, now set the new state and the SPINE read handler -func (c *ShipConnection) approveHandshake() { +func (c *ShipConnectionImpl) approveHandshake() { // Report to SPINE local device about this remote device connection - c.spineDataProcessing = c.deviceLocalCon.AddRemoteDevice(c.RemoteSKI, c) + c.spineDataProcessing = c.deviceLocalCon.AddRemoteDevice(c.remoteSKI, c) c.stopHandshakeTimer() c.setState(SmeStateComplete, nil) } // end the handshake process because of an error -func (c *ShipConnection) endHandshakeWithError(err error) { +func (c *ShipConnectionImpl) endHandshakeWithError(err error) { c.stopHandshakeTimer() c.setState(SmeStateError, err) @@ -218,11 +218,11 @@ func (c *ShipConnection) endHandshakeWithError(err error) { State: SmeStateError, Error: err, } - c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.RemoteSKI, state) + c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.remoteSKI, state) } // set the handshake timer to a new duration and start the channel -func (c *ShipConnection) setHandshakeTimer(timerType timeoutTimerType, duration time.Duration) { +func (c *ShipConnectionImpl) setHandshakeTimer(timerType timeoutTimerType, duration time.Duration) { c.stopHandshakeTimer() c.setHandshakeTimerRunning(true) @@ -241,7 +241,7 @@ func (c *ShipConnection) setHandshakeTimer(timerType timeoutTimerType, duration } // stop the handshake timer and close the channel -func (c *ShipConnection) stopHandshakeTimer() { +func (c *ShipConnectionImpl) stopHandshakeTimer() { if !c.getHandshakeTimerRunnging() { return } @@ -253,28 +253,28 @@ func (c *ShipConnection) stopHandshakeTimer() { c.setHandshakeTimerRunning(false) } -func (c *ShipConnection) setHandshakeTimerRunning(value bool) { +func (c *ShipConnectionImpl) setHandshakeTimerRunning(value bool) { c.handshakeTimerMux.Lock() defer c.handshakeTimerMux.Unlock() c.handshakeTimerRunning = value } -func (c *ShipConnection) getHandshakeTimerRunnging() bool { +func (c *ShipConnectionImpl) getHandshakeTimerRunnging() bool { c.handshakeTimerMux.Lock() defer c.handshakeTimerMux.Unlock() return c.handshakeTimerRunning } -func (c *ShipConnection) setHandshakeTimerType(timerType timeoutTimerType) { +func (c *ShipConnectionImpl) setHandshakeTimerType(timerType timeoutTimerType) { c.handshakeTimerMux.Lock() defer c.handshakeTimerMux.Unlock() c.handshakeTimerType = timerType } -func (c *ShipConnection) getHandshakeTimerType() timeoutTimerType { +func (c *ShipConnectionImpl) getHandshakeTimerType() timeoutTimerType { c.handshakeTimerMux.Lock() defer c.handshakeTimerMux.Unlock() diff --git a/ship/hs_access.go b/ship/hs_access.go index 81155a28..65814b16 100644 --- a/ship/hs_access.go +++ b/ship/hs_access.go @@ -11,7 +11,7 @@ import ( // Handshake Access covers the states smeAccess... -func (c *ShipConnection) handshakeAccessMethods_Init() { +func (c *ShipConnectionImpl) handshakeAccessMethods_Init() { // Access Methods accessMethodsRequest := model.AccessMethodsRequest{ AccessMethodsRequest: model.AccessMethodsRequestType{}, @@ -26,7 +26,7 @@ func (c *ShipConnection) handshakeAccessMethods_Init() { c.setState(SmeAccessMethodsRequest, nil) } -func (c *ShipConnection) handshakeAccessMethods_Request(message []byte) { +func (c *ShipConnectionImpl) handshakeAccessMethods_Request(message []byte) { _, data := c.parseMessage(message, true) dataString := string(data) @@ -68,7 +68,7 @@ func (c *ShipConnection) handshakeAccessMethods_Request(message []byte) { if len(c.remoteShipID) == 0 { c.remoteShipID = *accessMethods.AccessMethods.Id - c.serviceDataProvider.ReportServiceShipID(c.RemoteSKI, c.remoteShipID) + c.serviceDataProvider.ReportServiceShipID(c.remoteSKI, c.remoteShipID) } } else { diff --git a/ship/hs_hello.go b/ship/hs_hello.go index 0fab7a50..45ca7795 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -11,7 +11,7 @@ import ( // Handshake Hello covers the states smeHello... // SME_HELLO_STATE_READY_INIT -func (c *ShipConnection) handshakeHello_Init() { +func (c *ShipConnectionImpl) handshakeHello_Init() { if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypeReady, tHelloInit, false); err != nil { c.setAndHandleState(SmeHelloStateAbort) return @@ -21,7 +21,7 @@ func (c *ShipConnection) handshakeHello_Init() { } // SME_HELLO_STATE_READY_LISTEN -func (c *ShipConnection) handshakeHello_ReadyListen(timeout bool, message []byte) { +func (c *ShipConnectionImpl) handshakeHello_ReadyListen(timeout bool, message []byte) { if timeout { c.handshakeHello_ReadyTimeout() return @@ -77,12 +77,12 @@ func (c *ShipConnection) handshakeHello_ReadyListen(timeout bool, message []byte c.handleState(false, nil) } -func (c *ShipConnection) handshakeHello_ReadyTimeout() { +func (c *ShipConnectionImpl) handshakeHello_ReadyTimeout() { c.setAndHandleState(SmeHelloStateAbort) } // SME_HELLO_ABORT -func (c *ShipConnection) handshakeHello_Abort() { +func (c *ShipConnectionImpl) handshakeHello_Abort() { c.stopHandshakeTimer() if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypeAborted, 0, false); err != nil { @@ -94,7 +94,7 @@ func (c *ShipConnection) handshakeHello_Abort() { } // SME_HELLO_PENDING_INIT -func (c *ShipConnection) handshakeHello_PendingInit() { +func (c *ShipConnectionImpl) handshakeHello_PendingInit() { if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, tHelloInit, false); err != nil { c.endHandshakeWithError(err) return @@ -108,7 +108,7 @@ func (c *ShipConnection) handshakeHello_PendingInit() { } // SME_HELLO_PENDING_LISTEN -func (c *ShipConnection) handshakeHello_PendingListen(timeout bool, message []byte) { +func (c *ShipConnectionImpl) handshakeHello_PendingListen(timeout bool, message []byte) { if timeout { // The device needs to be in a state for the user to allow trusting the device // e.g. either the web UI or by other means @@ -209,7 +209,7 @@ func (c *ShipConnection) handshakeHello_PendingListen(timeout bool, message []by c.handleState(false, nil) } -func (c *ShipConnection) handshakeHello_PendingProlongationRequest() { +func (c *ShipConnectionImpl) handshakeHello_PendingProlongationRequest() { if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, 0, true); err != nil { c.endHandshakeWithError(err) return @@ -219,7 +219,7 @@ func (c *ShipConnection) handshakeHello_PendingProlongationRequest() { c.setHandshakeTimer(timeoutTimerTypeProlongRequestReply, tHelloInit) } -func (c *ShipConnection) handshakeHello_PendingTimeout() { +func (c *ShipConnectionImpl) handshakeHello_PendingTimeout() { if c.getHandshakeTimerType() != timeoutTimerTypeSendProlongationRequest { c.setAndHandleState(SmeHelloStateAbort) return @@ -237,7 +237,7 @@ func (c *ShipConnection) handshakeHello_PendingTimeout() { c.setHandshakeTimer(timeoutTimerTypeProlongRequestReply, c.lastReceivedWaitingValue) } -func (c *ShipConnection) handshakeHelloSend(phase model.ConnectionHelloPhaseType, waitingDuration time.Duration, prolongation bool) error { +func (c *ShipConnectionImpl) handshakeHelloSend(phase model.ConnectionHelloPhaseType, waitingDuration time.Duration, prolongation bool) error { helloMsg := model.ConnectionHello{ ConnectionHello: model.ConnectionHelloType{ Phase: phase, diff --git a/ship/hs_helper_test.go b/ship/hs_helper_test.go index ca86f32d..859a7011 100644 --- a/ship/hs_helper_test.go +++ b/ship/hs_helper_test.go @@ -44,7 +44,7 @@ func (w *dataHandlerTest) IsDataConnectionClosed() (bool, error) { return false, var _ ShipServiceDataProvider = (*dataHandlerTest)(nil) func (s *dataHandlerTest) IsRemoteServiceForSKIPaired(string) bool { return true } -func (s *dataHandlerTest) HandleConnectionClosed(*ShipConnection, bool) { +func (s *dataHandlerTest) HandleConnectionClosed(ShipConnection, bool) { s.handleConnectionClosedInvoked = true } func (s *dataHandlerTest) ReportServiceShipID(string, string) {} @@ -53,7 +53,7 @@ func (s *dataHandlerTest) AllowWaitingForTrust(string) bool { } func (s *dataHandlerTest) HandleShipHandshakeStateUpdate(string, ShipState) {} -func initTest(role shipRole) (*ShipConnection, *dataHandlerTest) { +func initTest(role shipRole) (*ShipConnectionImpl, *dataHandlerTest) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart, time.Second*4) @@ -63,6 +63,6 @@ func initTest(role shipRole) (*ShipConnection, *dataHandlerTest) { return conhandler, dataHandler } -func shutdownTest(conhandler *ShipConnection) { +func shutdownTest(conhandler *ShipConnectionImpl) { conhandler.stopHandshakeTimer() } diff --git a/ship/hs_init.go b/ship/hs_init.go index c55141ec..5060e4da 100644 --- a/ship/hs_init.go +++ b/ship/hs_init.go @@ -9,12 +9,12 @@ import ( // Handshake initialization covers the states cmiState... // CMI_STATE_INIT_START -func (c *ShipConnection) handshakeInit_cmiStateInitStart() { +func (c *ShipConnectionImpl) handshakeInit_cmiStateInitStart() { switch c.role { case ShipRoleClient: // CMI_STATE_CLIENT_SEND c.setState(CmiStateClientSend, nil) - if err := c.DataHandler.WriteMessageToDataConnection(shipInit); err != nil { + if err := c.dataHandler.WriteMessageToDataConnection(shipInit); err != nil { c.endHandshakeWithError(err) return } @@ -27,14 +27,14 @@ func (c *ShipConnection) handshakeInit_cmiStateInitStart() { } // CMI_STATE_SERVER_WAIT -func (c *ShipConnection) handshakeInit_cmiStateServerWait(message []byte) { +func (c *ShipConnectionImpl) handshakeInit_cmiStateServerWait(message []byte) { c.setState(CmiStateServerEvaluate, nil) if !c.handshakeInit_cmiStateEvaluate(message) { return } - if err := c.DataHandler.WriteMessageToDataConnection(shipInit); err != nil { + if err := c.dataHandler.WriteMessageToDataConnection(shipInit); err != nil { c.endHandshakeWithError(err) return } @@ -43,7 +43,7 @@ func (c *ShipConnection) handshakeInit_cmiStateServerWait(message []byte) { } // CMI_STATE_CLIENT_WAIT -func (c *ShipConnection) handshakeInit_cmiStateClientWait(message []byte) { +func (c *ShipConnectionImpl) handshakeInit_cmiStateClientWait(message []byte) { c.setState(CmiStateClientEvaluate, nil) if !c.handshakeInit_cmiStateEvaluate(message) { @@ -56,7 +56,7 @@ func (c *ShipConnection) handshakeInit_cmiStateClientWait(message []byte) { // CMI_STATE_SERVER_EVALUATE // CMI_STATE_CLIENT_EVALUATE // returns false in case of an error -func (c *ShipConnection) handshakeInit_cmiStateEvaluate(message []byte) bool { +func (c *ShipConnectionImpl) handshakeInit_cmiStateEvaluate(message []byte) bool { msgType, data := c.parseMessage(message, false) if msgType != model.MsgTypeInit { diff --git a/ship/hs_pin.go b/ship/hs_pin.go index 90b6f462..8a9c91d3 100644 --- a/ship/hs_pin.go +++ b/ship/hs_pin.go @@ -9,7 +9,7 @@ import ( // Handshake Pin covers the states smePin... -func (c *ShipConnection) handshakePin_Init() { +func (c *ShipConnectionImpl) handshakePin_Init() { c.setState(SmePinStateCheckInit, nil) pinState := model.ConnectionPinState{ @@ -26,7 +26,7 @@ func (c *ShipConnection) handshakePin_Init() { c.setState(SmePinStateCheckListen, nil) } -func (c *ShipConnection) handshakePin_smePinStateCheckListen(message []byte) { +func (c *ShipConnectionImpl) handshakePin_smePinStateCheckListen(message []byte) { _, data := c.parseMessage(message, true) var connectionPinState model.ConnectionPinState diff --git a/ship/hs_prot.go b/ship/hs_prot.go index fe467c29..318c4228 100644 --- a/ship/hs_prot.go +++ b/ship/hs_prot.go @@ -10,7 +10,7 @@ import ( // Handshake Prot covers the states smeProt... -func (c *ShipConnection) handshakeProtocol_Init() { +func (c *ShipConnectionImpl) handshakeProtocol_Init() { switch c.role { case ShipRoleServer: c.setState(SmeProtHStateServerInit, nil) @@ -23,7 +23,7 @@ func (c *ShipConnection) handshakeProtocol_Init() { } // provide a ship.MessageProtocolHandshake struct -func (c *ShipConnection) protocolHandshake() model.MessageProtocolHandshake { +func (c *ShipConnectionImpl) protocolHandshake() model.MessageProtocolHandshake { protocolHandshake := model.MessageProtocolHandshake{ MessageProtocolHandshake: model.MessageProtocolHandshakeType{ Version: model.Version{Major: 1, Minor: 0}, @@ -36,7 +36,7 @@ func (c *ShipConnection) protocolHandshake() model.MessageProtocolHandshake { return protocolHandshake } -func (c *ShipConnection) handshakeProtocol_smeProtHStateServerListenProposal(message []byte) { +func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateServerListenProposal(message []byte) { _, data := c.parseMessage(message, true) messageProtocolHandshake := model.MessageProtocolHandshake{} @@ -64,7 +64,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateServerListenProposal(mes c.setState(SmeProtHStateServerListenConfirm, nil) } -func (c *ShipConnection) handshakeProtocol_smeProtHStateServerListenConfirm(message []byte) { +func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateServerListenConfirm(message []byte) { _, data := c.parseMessage(message, true) var messageProtocolHandshake model.MessageProtocolHandshake @@ -85,7 +85,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateServerListenConfirm(mess c.setAndHandleState(SmeProtHStateServerOk) } -func (c *ShipConnection) handshakeProtocol_smeProtHStateClientInit() { +func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateClientInit() { c.setState(SmeProtHStateClientInit, nil) protocolHandshake := c.protocolHandshake() @@ -99,7 +99,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateClientInit() { c.setState(SmeProtHStateClientListenChoice, nil) } -func (c *ShipConnection) handshakeProtocol_smeProtHStateClientListenChoice(message []byte) { +func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateClientListenChoice(message []byte) { _, data := c.parseMessage(message, true) messageProtocolHandshake := model.MessageProtocolHandshake{} @@ -160,7 +160,7 @@ func (c *ShipConnection) handshakeProtocol_smeProtHStateClientListenChoice(messa c.setAndHandleState(SmeProtHStateClientOk) } -func (c *ShipConnection) abortProtocolHandshake(err model.MessageProtocolHandshakeErrorErrorType) { +func (c *ShipConnectionImpl) abortProtocolHandshake(err model.MessageProtocolHandshakeErrorErrorType) { c.stopHandshakeTimer() msg := model.MessageProtocolHandshakeError{ diff --git a/ship/mock_types_test.go b/ship/mock_types_test.go index 24c2cb96..c1badea2 100644 --- a/ship/mock_types_test.go +++ b/ship/mock_types_test.go @@ -171,7 +171,7 @@ func (mr *MockShipServiceDataProviderMockRecorder) AllowWaitingForTrust(arg0 int } // HandleConnectionClosed mocks base method. -func (m *MockShipServiceDataProvider) HandleConnectionClosed(arg0 *ShipConnection, arg1 bool) { +func (m *MockShipServiceDataProvider) HandleConnectionClosed(arg0 ShipConnection, arg1 bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "HandleConnectionClosed", arg0, arg1) } diff --git a/ship/mocks/ShipConnection.go b/ship/mocks/ShipConnection.go new file mode 100644 index 00000000..f9b0f582 --- /dev/null +++ b/ship/mocks/ShipConnection.go @@ -0,0 +1,108 @@ +// Code generated by mockery v2.39.1. DO NOT EDIT. + +package mocks + +import ( + ship "github.com/enbility/eebus-go/ship" + mock "github.com/stretchr/testify/mock" +) + +// ShipConnection is an autogenerated mock type for the ShipConnection type +type ShipConnection struct { + mock.Mock +} + +// AbortPendingHandshake provides a mock function with given fields: +func (_m *ShipConnection) AbortPendingHandshake() { + _m.Called() +} + +// ApprovePendingHandshake provides a mock function with given fields: +func (_m *ShipConnection) ApprovePendingHandshake() { + _m.Called() +} + +// CloseConnection provides a mock function with given fields: safe, code, reason +func (_m *ShipConnection) CloseConnection(safe bool, code int, reason string) { + _m.Called(safe, code, reason) +} + +// DataHandler provides a mock function with given fields: +func (_m *ShipConnection) DataHandler() ship.ShipDataConnection { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for DataHandler") + } + + var r0 ship.ShipDataConnection + if rf, ok := ret.Get(0).(func() ship.ShipDataConnection); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ship.ShipDataConnection) + } + } + + return r0 +} + +// RemoteSKI provides a mock function with given fields: +func (_m *ShipConnection) RemoteSKI() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for RemoteSKI") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// ShipHandshakeState provides a mock function with given fields: +func (_m *ShipConnection) ShipHandshakeState() (ship.ShipMessageExchangeState, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ShipHandshakeState") + } + + var r0 ship.ShipMessageExchangeState + var r1 error + if rf, ok := ret.Get(0).(func() (ship.ShipMessageExchangeState, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() ship.ShipMessageExchangeState); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(ship.ShipMessageExchangeState) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewShipConnection creates a new instance of ShipConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewShipConnection(t interface { + mock.TestingT + Cleanup(func()) +}) *ShipConnection { + mock := &ShipConnection{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/ship/mocks/ShipDataConnection.go b/ship/mocks/ShipDataConnection.go new file mode 100644 index 00000000..645072ce --- /dev/null +++ b/ship/mocks/ShipDataConnection.go @@ -0,0 +1,83 @@ +// Code generated by mockery v2.39.1. DO NOT EDIT. + +package mocks + +import ( + ship "github.com/enbility/eebus-go/ship" + mock "github.com/stretchr/testify/mock" +) + +// ShipDataConnection is an autogenerated mock type for the ShipDataConnection type +type ShipDataConnection struct { + mock.Mock +} + +// CloseDataConnection provides a mock function with given fields: closeCode, reason +func (_m *ShipDataConnection) CloseDataConnection(closeCode int, reason string) { + _m.Called(closeCode, reason) +} + +// InitDataProcessing provides a mock function with given fields: _a0 +func (_m *ShipDataConnection) InitDataProcessing(_a0 ship.ShipDataProcessing) { + _m.Called(_a0) +} + +// IsDataConnectionClosed provides a mock function with given fields: +func (_m *ShipDataConnection) IsDataConnectionClosed() (bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for IsDataConnectionClosed") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func() (bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// WriteMessageToDataConnection provides a mock function with given fields: _a0 +func (_m *ShipDataConnection) WriteMessageToDataConnection(_a0 []byte) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for WriteMessageToDataConnection") + } + + var r0 error + if rf, ok := ret.Get(0).(func([]byte) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewShipDataConnection creates a new instance of ShipDataConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewShipDataConnection(t interface { + mock.TestingT + Cleanup(func()) +}) *ShipDataConnection { + mock := &ShipDataConnection{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/ship/types.go b/ship/types.go index 65595197..075b04d3 100644 --- a/ship/types.go +++ b/ship/types.go @@ -111,6 +111,17 @@ const ( var shipInit []byte = []byte{model.MsgTypeInit, 0x00} //go:generate mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider +//go:generate mockery --name=ShipDataConnection +//go:generate mockery --name=ShipConnection + +type ShipConnection interface { + DataHandler() ShipDataConnection + CloseConnection(safe bool, code int, reason string) + RemoteSKI() string + ApprovePendingHandshake() + AbortPendingHandshake() + ShipHandshakeState() (ShipMessageExchangeState, error) +} // interface for handling the actual remote device data connection // @@ -149,7 +160,7 @@ type ShipServiceDataProvider interface { IsRemoteServiceForSKIPaired(string) bool // report closing of a connection and if handshake did complete - HandleConnectionClosed(*ShipConnection, bool) + HandleConnectionClosed(ShipConnection, bool) // report the ship ID provided during the handshake ReportServiceShipID(string, string) From 36642b03bdd3186a37b7168bed58c33fc81bd8ac Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 18:57:28 +0100 Subject: [PATCH 112/240] Add minor tests --- ship/connection_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ship/connection_test.go b/ship/connection_test.go index 0587bae2..9218d26f 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -57,6 +57,16 @@ func (s *ConnectionSuite) BeforeTest(suiteName, testName string) { s.sut = NewConnectionHandler(s.shipDataProvider, s.shipDataConn, localDevice, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID") } +func (s *ConnectionSuite) Test_RemoteSKI() { + remoteSki := s.sut.RemoteSKI() + assert.Equal(s.T(), s.sut.remoteSKI, remoteSki) +} + +func (s *ConnectionSuite) Test_DataHandler() { + handler := s.sut.DataHandler() + assert.NotNil(s.T(), handler) +} + func (s *ConnectionSuite) TestRun() { s.sut.Run() assert.Equal(s.T(), CmiStateServerWait, s.sut.smeState) From 30bc64372bce646c18649a4701c8247214f003ae Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 19:04:29 +0100 Subject: [PATCH 113/240] Add more binding manager tests --- spine/binding_manager_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spine/binding_manager_test.go b/spine/binding_manager_test.go index 3abd39d8..c1195c5f 100644 --- a/spine/binding_manager_test.go +++ b/spine/binding_manager_test.go @@ -69,6 +69,18 @@ func (suite *BindingManagerSuite) Test_Bindings() { subs = bindingMgr.Bindings(suite.remoteDevice) assert.Equal(suite.T(), 1, len(subs)) + address := model.FeatureAddressType{ + Device: entity.Device().Address(), + Entity: entity.Address().Entity, + Feature: util.Ptr(model.AddressFeatureType(10)), + } + entries := bindingMgr.BindingsOnFeature(address) + assert.Equal(suite.T(), 0, len(entries)) + + address.Feature = localFeature.Address().Feature + entries = bindingMgr.BindingsOnFeature(address) + assert.Equal(suite.T(), 1, len(entries)) + bindingDelete := model.BindingManagementDeleteCallType{ ClientAddress: remoteFeature.Address(), ServerAddress: localFeature.Address(), From 2066c8f31ae7715d9193d5b41d7e60a8ca0346c2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 19:11:59 +0100 Subject: [PATCH 114/240] Add spine usecase tests --- spine/usecase_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 spine/usecase_test.go diff --git a/spine/usecase_test.go b/spine/usecase_test.go new file mode 100644 index 00000000..f6273972 --- /dev/null +++ b/spine/usecase_test.go @@ -0,0 +1,42 @@ +package spine_test + +import ( + "testing" + "time" + + "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestUsecaseSuite(t *testing.T) { + suite.Run(t, new(UsecaseSuite)) +} + +type UsecaseSuite struct { + suite.Suite + + device *spine.DeviceLocalImpl + entity *spine.EntityLocalImpl +} + +func (s *UsecaseSuite) BeforeTest(suiteName, testName string) { + s.device = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + s.entity = spine.NewEntityLocalImpl(s.device, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + s.device.AddEntity(s.entity) + +} + +func (s *UsecaseSuite) Test_UseCase() { + uc := spine.NewUseCase( + s.entity, + model.UseCaseNameTypeControlOfBattery, + model.SpecificationVersionType("1.0.0"), + true, + []model.UseCaseScenarioSupportType{1}, + ) + assert.NotNil(s.T(), uc) + + uc.SetUseCaseAvailable(true) +} From 7590ab7dc4e8572165794d8c0cccae55fc4b52df Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 19:39:01 +0100 Subject: [PATCH 115/240] Minor fix --- spine/device_local.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spine/device_local.go b/spine/device_local.go index 5c33057c..1a7dc01d 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -248,8 +248,8 @@ func (r *DeviceLocalImpl) ProcessCmd(datagram model.DatagramType, remoteDevice * return nil } -func (r *DeviceLocalImpl) NodeManagement() NodeManagementImpl { - return *r.nodeManagement +func (r *DeviceLocalImpl) NodeManagement() *NodeManagementImpl { + return r.nodeManagement } func (r *DeviceLocalImpl) SubscriptionManager() SubscriptionManager { From e0c054c94ea1ba26b6e805804b291e2f56e4cfe9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 19:45:41 +0100 Subject: [PATCH 116/240] Refactor logging to make it thread safe --- logging/log.go | 17 +++++++++++++++-- service/hub.go | 34 ++++++++++++++++----------------- service/mdns.go | 12 ++++++------ service/mdns/avahi.go | 12 ++++++------ service/mdns/zeroconf.go | 2 +- service/service.go | 2 +- service/service_test.go | 6 +++--- ship/connection.go | 8 ++++---- ship/handshake.go | 4 ++-- ship/hs_hello.go | 4 ++-- ship/hs_prot.go | 18 ++++++++--------- ship/websocket.go | 10 +++++----- spine/device_local.go | 2 +- spine/device_remote.go | 2 +- spine/feature_local.go | 2 +- spine/feature_remote.go | 2 +- spine/function_data.go | 2 +- spine/model/eebus_tags.go | 2 +- spine/nodemanagement_usecase.go | 6 +++--- spine/send.go | 2 +- 20 files changed, 81 insertions(+), 68 deletions(-) diff --git a/logging/log.go b/logging/log.go index 7ddb1611..c3cf65c2 100644 --- a/logging/log.go +++ b/logging/log.go @@ -1,5 +1,7 @@ package logging +import "sync" + //go:generate mockery --name=Logging // Logging needs to be implemented, if the internal logs should be printed @@ -26,7 +28,8 @@ func (l *NoLogging) Infof(format string, args ...interface{}) {} func (l *NoLogging) Error(args ...interface{}) {} func (l *NoLogging) Errorf(format string, args ...interface{}) {} -var Log Logging = &NoLogging{} +var log Logging = &NoLogging{} +var mux sync.Mutex // Sets a custom logging implementation // By default NoLogging is used, so no logs are printed @@ -35,5 +38,15 @@ func SetLogging(logger Logging) { if logger == nil { return } - Log = logger + mux.Lock() + defer mux.Unlock() + + log = logger +} + +func Log() Logging { + mux.Lock() + defer mux.Unlock() + + return log } diff --git a/service/hub.go b/service/hub.go index bf4d61bf..7928579c 100644 --- a/service/hub.go +++ b/service/hub.go @@ -132,13 +132,13 @@ func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineL func (h *connectionsHubImpl) Start() { // start the websocket server if err := h.startWebsocketServer(); err != nil { - logging.Log.Debug("error during websocket server starting:", err) + logging.Log().Debug("error during websocket server starting:", err) } // start mDNS err := h.mdns.SetupMdnsService() if err != nil { - logging.Log.Debug("error during mdns setup:", err) + logging.Log().Debug("error during mdns setup:", err) } h.checkRestartMdnsSearch() @@ -398,7 +398,7 @@ func (h *connectionsHubImpl) verifyPeerCertificate(rawCerts [][]byte, verifiedCh // start the ship websocket server func (h *connectionsHubImpl) startWebsocketServer() error { addr := fmt.Sprintf(":%d", h.configuration.port) - logging.Log.Debug("starting websocket server on", addr) + logging.Log().Debug("starting websocket server on", addr) h.httpServer = &http.Server{ Addr: addr, @@ -413,7 +413,7 @@ func (h *connectionsHubImpl) startWebsocketServer() error { go func() { if err := h.httpServer.ListenAndServeTLS("", ""); err != nil { - logging.Log.Debug("websocket server error:", err) + logging.Log().Debug("websocket server error:", err) // TODO: decide how to handle this case } }() @@ -434,34 +434,34 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { - logging.Log.Debug("error during connection upgrading:", err) + logging.Log().Debug("error during connection upgrading:", err) return } // check if the client supports the ship sub protocol if conn.Subprotocol() != shipWebsocketSubProtocol { - logging.Log.Debug("client does not support the ship sub protocol") + logging.Log().Debug("client does not support the ship sub protocol") _ = conn.Close() return } // check if the clients certificate provides a SKI if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 { - logging.Log.Debug("client does not provide a certificate") + logging.Log().Debug("client does not provide a certificate") _ = conn.Close() return } ski, err := skiFromCertificate(r.TLS.PeerCertificates[0]) if err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) _ = conn.Close() return } // normalize the incoming SKI remoteService := NewServiceDetails(ski) - logging.Log.Debug("incoming connection request from", remoteService.SKI) + logging.Log().Debug("incoming connection request from", remoteService.SKI) // Check if the remote service is paired service := h.ServiceForSKI(remoteService.SKI) @@ -495,7 +495,7 @@ func (h *connectionsHubImpl) connectFoundService(remoteService *ServiceDetails, return nil } - logging.Log.Debugf("initiating connection to %s at %s:%s", remoteService.SKI, host, port) + logging.Log().Debugf("initiating connection to %s at %s:%s", remoteService.SKI, host, port) dialer := &websocket.Dialer{ Proxy: http.ProxyFromEnvironment, @@ -584,14 +584,14 @@ func (h *connectionsHubImpl) keepThisConnection(conn *websocket.Conn, incomingRe if keep { // we have an existing connection // so keep the new (most recent) and close the old one - logging.Log.Debug("closing existing double connection") + logging.Log().Debug("closing existing double connection") go existingC.CloseConnection(false, 0, "") } else { connType := "incoming" if !incomingRequest { connType = "outgoing" } - logging.Log.Debugf("closing %s double connection, as the existing connection will be used", connType) + logging.Log().Debugf("closing %s double connection, as the existing connection will be used", connType) if conn != nil { go func() { _ = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "double connection")) @@ -741,7 +741,7 @@ func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *M return } - logging.Log.Debugf("delaying connection to %s by %s to minimize double connection probability", ski, duration) + logging.Log().Debugf("delaying connection to %s by %s to minimize double connection probability", ski, duration) // we do not stop this thread and just let the timer run out // otherwise we would need a stop channel for each ski @@ -799,9 +799,9 @@ func (h *connectionsHubImpl) initateConnection(remoteService *ServiceDetails, en return false } - logging.Log.Debug("trying to connect to", remoteService.SKI, "at", address) + logging.Log().Debug("trying to connect to", remoteService.SKI, "at", address) if err = h.connectFoundService(remoteService, address.String(), strconv.Itoa(entry.Port)); err != nil { - logging.Log.Debug("connection to", remoteService.SKI, "failed: ", err) + logging.Log().Debug("connection to", remoteService.SKI, "failed: ", err) } else { return true } @@ -809,9 +809,9 @@ func (h *connectionsHubImpl) initateConnection(remoteService *ServiceDetails, en // connectdion via IP address failed, try hostname if len(entry.Host) > 0 { - logging.Log.Debug("trying to connect to", remoteService.SKI, "at", entry.Host) + logging.Log().Debug("trying to connect to", remoteService.SKI, "at", entry.Host) if err = h.connectFoundService(remoteService, entry.Host, strconv.Itoa(entry.Port)); err != nil { - logging.Log.Debugf("connection to %s failed: %s", remoteService.SKI, err) + logging.Log().Debugf("connection to %s failed: %s", remoteService.SKI, err) } else { return true } diff --git a/service/mdns.go b/service/mdns.go index 7649b277..369ffb84 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -162,12 +162,12 @@ func (m *mdnsManager) AnnounceMdnsEntry() error { "register=" + fmt.Sprintf("%v", m.configuration.registerAutoAccept), } - logging.Log.Debug("mdns: announce") + logging.Log().Debug("mdns: announce") serviceName := m.configuration.MdnsServiceName() if err := m.mdnsProvider.Announce(serviceName, m.configuration.port, txt); err != nil { - logging.Log.Debug("mdns: failure announcing service", err) + logging.Log().Debug("mdns: failure announcing service", err) return err } @@ -183,7 +183,7 @@ func (m *mdnsManager) UnannounceMdnsEntry() { } m.mdnsProvider.Unannounce() - logging.Log.Debug("mdns: stop announcement") + logging.Log().Debug("mdns: stop announcement") m.isAnnounced = false } @@ -289,7 +289,7 @@ func (m *mdnsManager) resolveEntries() { return } go func() { - logging.Log.Debug("mdns: start search") + logging.Log().Debug("mdns: start search") m.mdnsProvider.ResolveEntries(m.cancelChan, m.processMdnsEntry) m.setIsSearchingServices(false) @@ -306,7 +306,7 @@ func (m *mdnsManager) stopResolvingEntries() { return } - logging.Log.Debug("mdns: stop search") + logging.Log().Debug("mdns: stop search") m.cancelChan <- true } @@ -407,7 +407,7 @@ func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host st } m.setMdnsEntry(ski, newEntry) - logging.Log.Debug("ski:", ski, "name:", name, "brand:", brand, "model:", model, "typ:", deviceType, "identifier:", identifier, "register:", register, "host:", host, "port:", port, "addresses:", addresses) + logging.Log().Debug("ski:", ski, "name:", name, "brand:", brand, "model:", model, "typ:", deviceType, "identifier:", identifier, "register:", register, "host:", host, "port:", port, "addresses:", addresses) } else { return } diff --git a/service/mdns/avahi.go b/service/mdns/avahi.go index 1f0e5c5b..4cba47f4 100644 --- a/service/mdns/avahi.go +++ b/service/mdns/avahi.go @@ -62,7 +62,7 @@ func (a *AvahiProvider) Shutdown() { } func (a *AvahiProvider) Announce(serviceName string, port int, txt []string) error { - logging.Log.Debug("mdns: using avahi") + logging.Log().Debug("mdns: using avahi") entryGroup, err := a.avServer.EntryGroupNew() if err != nil { @@ -108,12 +108,12 @@ func (a *AvahiProvider) ResolveEntries(cancelChan chan bool, callback func(eleme // instead of limiting search on specific allowed interfaces, we allow all and filter the results if avBrowser, err = a.avServer.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0); err != nil { - logging.Log.Debug("mdns: error setting up avahi browser:", err) + logging.Log().Debug("mdns: error setting up avahi browser:", err) return } if avBrowser == nil { - logging.Log.Debug("mdns: avahi browser is not available") + logging.Log().Debug("mdns: avahi browser is not available") return } @@ -149,13 +149,13 @@ func (a *AvahiProvider) processService(service avahi.Service, remove bool, callb } if !allow { - logging.Log.Debug("avahi - ignoring service as its interface is not in the allowed list:", service.Name) + logging.Log().Debug("avahi - ignoring service as its interface is not in the allowed list:", service.Name) return } resolved, err := a.avServer.ResolveService(service.Interface, service.Protocol, service.Name, service.Type, service.Domain, avahi.ProtoUnspec, 0) if err != nil { - logging.Log.Debug("avahi - error resolving service:", service, "error:", err) + logging.Log().Debug("avahi - error resolving service:", service, "error:", err) return } @@ -170,7 +170,7 @@ func (a *AvahiProvider) processService(service avahi.Service, remove bool, callb address := net.ParseIP(resolved.Address) // if the address can not be used, ignore the entry if address == nil || address.IsUnspecified() { - logging.Log.Debug("avahi - service provides unusable address:", service.Name) + logging.Log().Debug("avahi - service provides unusable address:", service.Name) return } diff --git a/service/mdns/zeroconf.go b/service/mdns/zeroconf.go index aa56f6e0..987762c2 100644 --- a/service/mdns/zeroconf.go +++ b/service/mdns/zeroconf.go @@ -29,7 +29,7 @@ func (z *ZeroconfProvider) CheckAvailability() bool { func (z *ZeroconfProvider) Shutdown() {} func (z *ZeroconfProvider) Announce(serviceName string, port int, txt []string) error { - logging.Log.Debug("mdns: using zeroconf") + logging.Log().Debug("mdns: using zeroconf") // use Zeroconf library if avahi is not available // Set TTL to 2 minutes as defined in SHIP chapter 7 diff --git a/service/service.go b/service/service.go index 7ecab2a0..6308a1d3 100644 --- a/service/service.go +++ b/service/service.go @@ -180,7 +180,7 @@ func (s *EEBUSService) Setup() error { s.LocalService.DeviceType = sd.deviceType s.LocalService.RegisterAutoAccept = sd.registerAutoAccept - logging.Log.Info("Local SKI: ", ski) + logging.Log().Info("Local SKI: ", ski) vendor := sd.vendorCode if vendor == "" { diff --git a/service/service_test.go b/service/service_test.go index 6318e133..5ff820dd 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -112,13 +112,13 @@ func (s *ServiceSuite) Test_ConnectionsHub() { func (s *ServiceSuite) Test_SetLogging() { s.sut.SetLogging(nil) - assert.Equal(s.T(), &logging.NoLogging{}, logging.Log) + assert.Equal(s.T(), &logging.NoLogging{}, logging.Log()) s.sut.SetLogging(s.logging) - assert.Equal(s.T(), s.logging, logging.Log) + assert.Equal(s.T(), s.logging, logging.Log()) s.sut.SetLogging(&logging.NoLogging{}) - assert.Equal(s.T(), &logging.NoLogging{}, logging.Log) + assert.Equal(s.T(), &logging.NoLogging{}, logging.Log()) } func (s *ServiceSuite) Test_Setup() { diff --git a/ship/connection.go b/ship/connection.go index b629c54c..0a4e8c71 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -197,7 +197,7 @@ var _ spine.SpineDataConnection = (*ShipConnectionImpl)(nil) // SpineDataConnection interface implementation func (c *ShipConnectionImpl) WriteSpineMessage(message []byte) { if err := c.sendSpineData(message); err != nil { - logging.Log.Debug(c.RemoteSKI, "Error sending spine message: ", err) + logging.Log().Debug(c.RemoteSKI, "Error sending spine message: ", err) return } } @@ -210,13 +210,13 @@ func (c *ShipConnectionImpl) shipModelFromMessage(message []byte) (*model.ShipDa // Get the datagram from the message data := model.ShipData{} if err := json.Unmarshal(jsonData, &data); err != nil { - logging.Log.Debug(c.RemoteSKI, "error unmarshalling message: ", err) + logging.Log().Debug(c.RemoteSKI, "error unmarshalling message: ", err) return nil, err } if data.Data.Payload == nil { errorMsg := "received no valid payload" - logging.Log.Debug(c.RemoteSKI, errorMsg) + logging.Log().Debug(c.RemoteSKI, errorMsg) return nil, errors.New(errorMsg) } @@ -342,7 +342,7 @@ func (c *ShipConnectionImpl) sendSpineData(data []byte) error { err = c.dataHandler.WriteMessageToDataConnection(shipMsg) if err != nil { - logging.Log.Debug("error sending message: ", err) + logging.Log().Debug("error sending message: ", err) return err } diff --git a/ship/handshake.go b/ship/handshake.go index 26889ca8..b2db2773 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -92,7 +92,7 @@ func (c *ShipConnectionImpl) getState() ShipMessageExchangeState { func (c *ShipConnectionImpl) handleState(timeout bool, message []byte) { switch c.getState() { case SmeStateError: - logging.Log.Debug(c.RemoteSKI, "connection is in error state") + logging.Log().Debug(c.RemoteSKI, "connection is in error state") return // cmiStateInit @@ -210,7 +210,7 @@ func (c *ShipConnectionImpl) endHandshakeWithError(err error) { c.setState(SmeStateError, err) - logging.Log.Debug(c.RemoteSKI, "SHIP handshake error:", err) + logging.Log().Debug(c.RemoteSKI, "SHIP handshake error:", err) c.CloseConnection(true, 0, err.Error()) diff --git a/ship/hs_hello.go b/ship/hs_hello.go index 45ca7795..6beaf811 100644 --- a/ship/hs_hello.go +++ b/ship/hs_hello.go @@ -69,7 +69,7 @@ func (c *ShipConnectionImpl) handshakeHello_ReadyListen(timeout bool, message [] default: // don't accept any other responses - logging.Log.Errorf("Unexpected connection hello phase: %s", hello.Phase) + logging.Log().Errorf("Unexpected connection hello phase: %s", hello.Phase) c.setAndHandleState(SmeHelloStateAbort) return } @@ -201,7 +201,7 @@ func (c *ShipConnectionImpl) handshakeHello_PendingListen(timeout bool, message default: // don't accept any other responses - logging.Log.Errorf("Unexpected connection hello phase: %s", hello.Phase) + logging.Log().Errorf("Unexpected connection hello phase: %s", hello.Phase) c.setAndHandleState(SmeHelloStateAbort) return } diff --git a/ship/hs_prot.go b/ship/hs_prot.go index 318c4228..0f5c6de9 100644 --- a/ship/hs_prot.go +++ b/ship/hs_prot.go @@ -69,13 +69,13 @@ func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateServerListenConfirm( var messageProtocolHandshake model.MessageProtocolHandshake if err := json.Unmarshal([]byte(data), &messageProtocolHandshake); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeUnexpectedMessage) return } if messageProtocolHandshake.MessageProtocolHandshake.HandshakeType != model.ProtocolHandshakeTypeTypeSelect { - logging.Log.Debug("invalid protocol handshake response") + logging.Log().Debug("invalid protocol handshake response") c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) return } @@ -104,7 +104,7 @@ func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateClientListenChoice(m messageProtocolHandshake := model.MessageProtocolHandshake{} if err := json.Unmarshal([]byte(data), &messageProtocolHandshake); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeUnexpectedMessage) return } @@ -113,32 +113,32 @@ func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateClientListenChoice(m abort := false if msgHandshake.HandshakeType != model.ProtocolHandshakeTypeTypeSelect { - logging.Log.Debug("invalid protocol handshake response") + logging.Log().Debug("invalid protocol handshake response") abort = true } if msgHandshake.Version.Major != 1 { - logging.Log.Debug("unsupported protocol major version") + logging.Log().Debug("unsupported protocol major version") abort = true } if msgHandshake.Version.Minor != 0 { - logging.Log.Debug("unsupported protocol minor version") + logging.Log().Debug("unsupported protocol minor version") abort = true } if msgHandshake.Formats.Format == nil || len(msgHandshake.Formats.Format) == 0 { - logging.Log.Debug("format is missing") + logging.Log().Debug("format is missing") abort = true } if len(msgHandshake.Formats.Format) != 1 { - logging.Log.Debug("unsupported format response") + logging.Log().Debug("unsupported format response") abort = true } if msgHandshake.Formats.Format != nil && msgHandshake.Formats.Format[0] != model.MessageProtocolFormatTypeUTF8 { - logging.Log.Debug("unsupported format") + logging.Log().Debug("unsupported format") abort = true } diff --git a/ship/websocket.go b/ship/websocket.go index e534eab1..7f11c07d 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -105,7 +105,7 @@ func (w *websocketConnection) writeShipPump() { _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) w.muxConWrite.Unlock() if !ok { - logging.Log.Debug(w.remoteSki, "ship write channel closed") + logging.Log().Debug(w.remoteSki, "ship write channel closed") // The write channel has been closed _ = w.writeMessage(websocket.CloseMessage, []byte{}) return @@ -129,7 +129,7 @@ func (w *websocketConnection) writeShipPump() { } else { text = "unknown single byte" } - logging.Log.Trace("Send:", w.remoteSki, text) + logging.Log().Trace("Send:", w.remoteSki, text) case <-ticker.C: w.handlePing() @@ -152,7 +152,7 @@ func (w *websocketConnection) handlePing() { } func (w *websocketConnection) closeWithError(err error, reason string) { - logging.Log.Debug(w.remoteSki, reason, err) + logging.Log().Debug(w.remoteSki, reason, err) w.setConnClosedError(err) w.dataProcessing.ReportConnectionError(err) } @@ -174,7 +174,7 @@ func (w *websocketConnection) readShipPump() { } if err != nil { - logging.Log.Debug(w.remoteSki, "websocket read error: ", err) + logging.Log().Debug(w.remoteSki, "websocket read error: ", err) w.close() w.setConnClosedError(err) w.dataProcessing.ReportConnectionError(err) @@ -189,7 +189,7 @@ func (w *websocketConnection) readShipPump() { } else { text = "unknown single byte" } - logging.Log.Trace("Recv:", w.remoteSki, text) + logging.Log().Trace("Recv:", w.remoteSki, text) w.dataProcessing.HandleIncomingShipMessage(message) } diff --git a/spine/device_local.go b/spine/device_local.go index 1a7dc01d..2c9c69b0 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -228,7 +228,7 @@ func (r *DeviceLocalImpl) ProcessCmd(datagram model.DatagramType, remoteDevice * remoteFeature.Type() } - logging.Log.Debug(datagram.PrintMessageOverview(false, lfType, rfType)) + logging.Log().Debug(datagram.PrintMessageOverview(false, lfType, rfType)) err := localFeature.HandleMessage(message) if err != nil { diff --git a/spine/device_remote.go b/spine/device_remote.go index 36cdc7a9..53656c37 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -55,7 +55,7 @@ func (d *DeviceRemoteImpl) HandleIncomingSpineMesssage(message []byte) (*model.M } err := d.localDevice.ProcessCmd(datagram.Datagram, d) if err != nil { - logging.Log.Trace(err) + logging.Log().Trace(err) } return datagram.Datagram.Header.MsgCounter, nil diff --git a/spine/feature_local.go b/spine/feature_local.go index 885e0a2e..c5c25737 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -393,7 +393,7 @@ func (r *FeatureLocalImpl) processResult(message *Message) *ErrorType { if message.Cmd.ResultData.Description != nil { errorString += fmt.Sprintf(": %s", *message.Cmd.ResultData.Description) } - logging.Log.Debug(errorString) + logging.Log().Debug(errorString) } // we don't need to populate this error as requests don't require a pendingRequest entry diff --git a/spine/feature_remote.go b/spine/feature_remote.go index ea82afa3..7fb8f0ab 100644 --- a/spine/feature_remote.go +++ b/spine/feature_remote.go @@ -82,7 +82,7 @@ func (r *FeatureRemoteImpl) SetMaxResponseDelay(delay *model.MaxResponseDelayTyp if err != nil { r.maxResponseDelay = util.Ptr(p.DurationApprox()) } else { - logging.Log.Debug(err) + logging.Log().Debug(err) } } diff --git a/spine/function_data.go b/spine/function_data.go index f077e8cc..b2719a5c 100644 --- a/spine/function_data.go +++ b/spine/function_data.go @@ -83,6 +83,6 @@ func (r *FunctionDataImpl[T]) DataAny() any { func (r *FunctionDataImpl[T]) UpdateDataAny(newData any, filterPartial *model.FilterType, filterDelete *model.FilterType) { err := r.UpdateData(newData.(*T), filterPartial, filterDelete) if err != nil { - logging.Log.Debug(err.String()) + logging.Log().Debug(err.String()) } } diff --git a/spine/model/eebus_tags.go b/spine/model/eebus_tags.go index 8e9d4241..ed0e95d2 100644 --- a/spine/model/eebus_tags.go +++ b/spine/model/eebus_tags.go @@ -38,7 +38,7 @@ func EEBusTags(field reflect.StructField) map[EEBusTag]string { } else if len(pair) == 2 { result[EEBusTag(pair[0])] = pair[1] } else { - logging.Log.Errorf("error: malformatted eebus tag: '%s'", tags) + logging.Log().Errorf("error: malformatted eebus tag: '%s'", tags) } } diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go index c6aae0c5..d80da232 100644 --- a/spine/nodemanagement_usecase.go +++ b/spine/nodemanagement_usecase.go @@ -58,7 +58,7 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data mode if useCaseInfo.Actor != nil { actor = model.UseCaseActorType(*useCaseInfo.Actor) } else { - logging.Log.Debug("actor is missing in useCaseInformation") + logging.Log().Debug("actor is missing in useCaseInformation") break } @@ -69,7 +69,7 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data mode if useCaseSupport.UseCaseName != nil { useCaseName = model.UseCaseNameType(*useCaseSupport.UseCaseName) } else { - logging.Log.Debug("useCaseName is missing in useCaseSupport") + logging.Log().Debug("useCaseName is missing in useCaseSupport") continue } @@ -85,7 +85,7 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data mode } if useCaseSupport.ScenarioSupport == nil { - logging.Log.Errorf("scenarioSupport is missing in useCaseSupport %s", useCaseName) + logging.Log().Errorf("scenarioSupport is missing in useCaseSupport %s", useCaseName) continue } diff --git a/spine/send.go b/spine/send.go index 29039b87..936cbe3b 100644 --- a/spine/send.go +++ b/spine/send.go @@ -87,7 +87,7 @@ func (c *SenderImpl) sendSpineMessage(datagram model.DatagramType) error { return errors.New("message is nil") } - logging.Log.Debug(datagram.PrintMessageOverview(true, "", "")) + logging.Log().Debug(datagram.PrintMessageOverview(true, "", "")) // write to channel c.writeHandler.WriteSpineMessage(msg) From 72de540873fd2f03c8f1f2308b8bd933965ecde6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:08:00 +0100 Subject: [PATCH 117/240] Move spine errortype to model package --- spine/device_local.go | 2 +- spine/feature_local.go | 72 +++++++++++------------ spine/function_data.go | 4 +- spine/mocks/Sender.go | 6 +- spine/{error.go => model/custom_error.go} | 19 +++--- spine/nodemanagement.go | 22 +++---- spine/nodemanagement_destinationlist.go | 4 +- spine/nodemanagement_detaileddiscovery.go | 2 +- spine/nodemanagement_usecase.go | 2 +- spine/pending_requests.go | 28 ++++----- spine/pending_requests_test.go | 10 ++-- spine/send.go | 6 +- 12 files changed, 87 insertions(+), 90 deletions(-) rename spine/{error.go => model/custom_error.go} (51%) diff --git a/spine/device_local.go b/spine/device_local.go index 2c9c69b0..fe93ff65 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -217,7 +217,7 @@ func (r *DeviceLocalImpl) ProcessCmd(datagram model.DatagramType, remoteDevice * if localFeature == nil { errorMessage := "invalid feature address" - _ = remoteFeature.Sender().ResultError(message.RequestHeader, destAddr, NewErrorType(model.ErrorNumberTypeDestinationUnknown, errorMessage)) + _ = remoteFeature.Sender().ResultError(message.RequestHeader, destAddr, model.NewErrorType(model.ErrorNumberTypeDestinationUnknown, errorMessage)) return errors.New(errorMessage) } diff --git a/spine/feature_local.go b/spine/feature_local.go index c5c25737..d23e3278 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -22,37 +22,37 @@ type FeatureLocal interface { function model.FunctionType, selector any, elements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *ErrorType) + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) RequestDataBySenderAddress( cmd model.CmdType, sender Sender, destinationSki string, destinationAddress *model.FeatureAddressType, - maxDelay time.Duration) (*model.MsgCounterType, *ErrorType) + maxDelay time.Duration) (*model.MsgCounterType, *model.ErrorType) FetchRequestData( msgCounter model.MsgCounterType, - destination *FeatureRemoteImpl) (any, *ErrorType) + destination *FeatureRemoteImpl) (any, *model.ErrorType) RequestAndFetchData( function model.FunctionType, selector any, elements any, - destination *FeatureRemoteImpl) (any, *ErrorType) - Subscribe(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *ErrorType) + destination *FeatureRemoteImpl) (any, *model.ErrorType) + Subscribe(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) // SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed - Bind(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *ErrorType) + Bind(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) // BindAndWait(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) *ErrorType NotifyData( function model.FunctionType, deleteSelector, partialSelector any, partialWithoutSelector bool, deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *ErrorType) + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) WriteData( function model.FunctionType, deleteSelector, partialSelector any, deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *ErrorType) - HandleMessage(message *Message) *ErrorType + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) + HandleMessage(message *Message) *model.ErrorType } type FeatureResult interface { @@ -185,7 +185,7 @@ func (r *FeatureLocalImpl) RequestData( function model.FunctionType, selector any, elements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *ErrorType) { + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) { fd := r.functionData(function) cmd := fd.ReadCmdType(selector, elements) @@ -197,7 +197,7 @@ func (r *FeatureLocalImpl) RequestDataBySenderAddress( sender Sender, deviceSki string, destinationAddress *model.FeatureAddressType, - maxDelay time.Duration) (*model.MsgCounterType, *ErrorType) { + maxDelay time.Duration) (*model.MsgCounterType, *model.ErrorType) { msgCounter, err := sender.Request(model.CmdClassifierTypeRead, r.Address(), destinationAddress, false, []model.CmdType{cmd}) if err == nil { @@ -205,14 +205,14 @@ func (r *FeatureLocalImpl) RequestDataBySenderAddress( return msgCounter, nil } - return msgCounter, NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return msgCounter, model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } // Wait and return the response from destination for a message with the msgCounter ID // this will block until the response is received func (r *FeatureLocalImpl) FetchRequestData( msgCounter model.MsgCounterType, - destination *FeatureRemoteImpl) (any, *ErrorType) { + destination *FeatureRemoteImpl) (any, *model.ErrorType) { return r.pendingRequests.GetData(destination.Device().ski, msgCounter) } @@ -223,7 +223,7 @@ func (r *FeatureLocalImpl) RequestAndFetchData( function model.FunctionType, selector any, elements any, - destination *FeatureRemoteImpl) (any, *ErrorType) { + destination *FeatureRemoteImpl) (any, *model.ErrorType) { msgCounter, err := r.RequestData(function, selector, elements, destination) if err != nil { @@ -234,14 +234,14 @@ func (r *FeatureLocalImpl) RequestAndFetchData( } // Subscribe to a remote feature -func (r *FeatureLocalImpl) Subscribe(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *ErrorType) { +func (r *FeatureLocalImpl) Subscribe(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { if r.Role() == model.RoleTypeServer { - return nil, NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) + return nil, model.NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) } msgCounter, err := remoteDevice.Sender().Subscribe(r.Address(), remoteAdress, r.ftype) if err != nil { - return nil, NewErrorTypeFromString(err.Error()) + return nil, model.NewErrorTypeFromString(err.Error()) } return msgCounter, nil @@ -276,14 +276,14 @@ func (r *FeatureLocalImpl) SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remo */ // Bind to a remote feature -func (r *FeatureLocalImpl) Bind(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *ErrorType) { +func (r *FeatureLocalImpl) Bind(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { if r.Role() == model.RoleTypeServer { - return nil, NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) + return nil, model.NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) } msgCounter, err := remoteDevice.Sender().Bind(r.Address(), remoteAddress, r.ftype) if err != nil { - return nil, NewErrorTypeFromString(err.Error()) + return nil, model.NewErrorTypeFromString(err.Error()) } return msgCounter, nil @@ -323,13 +323,13 @@ func (r *FeatureLocalImpl) NotifyData( deleteSelector, partialSelector any, partialWithoutSelector bool, deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *ErrorType) { + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) { fd := r.functionData(function) cmd := fd.NotifyCmdType(deleteSelector, partialSelector, partialWithoutSelector, deleteElements) msgCounter, err := destination.Sender().Request(model.CmdClassifierTypeRead, r.Address(), destination.Address(), false, []model.CmdType{cmd}) if err != nil { - return nil, NewErrorTypeFromString(err.Error()) + return nil, model.NewErrorTypeFromString(err.Error()) } return msgCounter, nil } @@ -339,29 +339,29 @@ func (r *FeatureLocalImpl) WriteData( function model.FunctionType, deleteSelector, partialSelector any, deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *ErrorType) { + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) { fd := r.functionData(function) cmd := fd.WriteCmdType(deleteSelector, partialSelector, deleteElements) msgCounter, err := destination.Sender().Write(r.Address(), destination.Address(), cmd) if err != nil { - return nil, NewErrorTypeFromString(err.Error()) + return nil, model.NewErrorTypeFromString(err.Error()) } return msgCounter, nil } -func (r *FeatureLocalImpl) HandleMessage(message *Message) *ErrorType { +func (r *FeatureLocalImpl) HandleMessage(message *Message) *model.ErrorType { if message.Cmd.ResultData != nil { return r.processResult(message) } cmdData, err := message.Cmd.Data() if err != nil { - return NewErrorType(model.ErrorNumberTypeCommandNotSupported, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeCommandNotSupported, err.Error()) } if cmdData.Function == nil { - return NewErrorType(model.ErrorNumberTypeCommandNotSupported, "No function found for cmd data") + return model.NewErrorType(model.ErrorNumberTypeCommandNotSupported, "No function found for cmd data") } switch message.CmdClassifier { @@ -378,13 +378,13 @@ func (r *FeatureLocalImpl) HandleMessage(message *Message) *ErrorType { return err } default: - return NewErrorTypeFromString(fmt.Sprintf("CmdClassifier not implemented: %s", message.CmdClassifier)) + return model.NewErrorTypeFromString(fmt.Sprintf("CmdClassifier not implemented: %s", message.CmdClassifier)) } return nil } -func (r *FeatureLocalImpl) processResult(message *Message) *ErrorType { +func (r *FeatureLocalImpl) processResult(message *Message) *model.ErrorType { switch message.CmdClassifier { case model.CmdClassifierTypeResult: if *message.Cmd.ResultData.ErrorNumber != model.ErrorNumberTypeNoError { @@ -397,7 +397,7 @@ func (r *FeatureLocalImpl) processResult(message *Message) *ErrorType { } // we don't need to populate this error as requests don't require a pendingRequest entry - _ = r.pendingRequests.SetResult(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference, NewErrorTypeFromResult(message.Cmd.ResultData)) + _ = r.pendingRequests.SetResult(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference, model.NewErrorTypeFromResult(message.Cmd.ResultData)) if message.RequestHeader.MsgCounterReference == nil { return nil @@ -423,28 +423,28 @@ func (r *FeatureLocalImpl) processResult(message *Message) *ErrorType { return nil default: - return NewErrorType( + return model.NewErrorType( model.ErrorNumberTypeGeneralError, fmt.Sprintf("ResultData CmdClassifierType %s not implemented", message.CmdClassifier)) } } -func (r *FeatureLocalImpl) processRead(function model.FunctionType, requestHeader *model.HeaderType, featureRemote *FeatureRemoteImpl) *ErrorType { +func (r *FeatureLocalImpl) processRead(function model.FunctionType, requestHeader *model.HeaderType, featureRemote *FeatureRemoteImpl) *model.ErrorType { // is this a read request to a local server/special feature? if r.role == model.RoleTypeClient { // Read requests to a client feature are not allowed - return NewErrorTypeFromNumber(model.ErrorNumberTypeCommandRejected) + return model.NewErrorTypeFromNumber(model.ErrorNumberTypeCommandRejected) } cmd := r.functionData(function).ReplyCmdType(false) if err := featureRemote.Sender().Reply(requestHeader, r.Address(), cmd); err != nil { - return NewErrorTypeFromString(err.Error()) + return model.NewErrorTypeFromString(err.Error()) } return nil } -func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, requestHeader *model.HeaderType, featureRemote *FeatureRemoteImpl) *ErrorType { +func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, requestHeader *model.HeaderType, featureRemote *FeatureRemoteImpl) *model.ErrorType { featureRemote.UpdateData(function, data, filterPartial, filterDelete) _ = r.pendingRequests.SetData(featureRemote.Device().ski, *requestHeader.MsgCounterReference, data) // an error in SetData only means that there is no pendingRequest waiting for this dataset @@ -466,7 +466,7 @@ func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, f return nil } -func (r *FeatureLocalImpl) processNotify(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, featureRemote *FeatureRemoteImpl) *ErrorType { +func (r *FeatureLocalImpl) processNotify(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, featureRemote *FeatureRemoteImpl) *model.ErrorType { featureRemote.UpdateData(function, data, filterPartial, filterDelete) payload := EventPayload{ diff --git a/spine/function_data.go b/spine/function_data.go index b2719a5c..9820d9ed 100644 --- a/spine/function_data.go +++ b/spine/function_data.go @@ -51,7 +51,7 @@ func (r *FunctionDataImpl[T]) Data() *T { return &copiedData } -func (r *FunctionDataImpl[T]) UpdateData(newData *T, filterPartial *model.FilterType, filterDelete *model.FilterType) *ErrorType { +func (r *FunctionDataImpl[T]) UpdateData(newData *T, filterPartial *model.FilterType, filterDelete *model.FilterType) *model.ErrorType { r.mux.Lock() defer r.mux.Unlock() @@ -63,7 +63,7 @@ func (r *FunctionDataImpl[T]) UpdateData(newData *T, filterPartial *model.Filter supported := util.Implements[T, model.Updater]() if !supported { - return NewErrorTypeFromString(fmt.Sprintf("partial updates are not supported for type '%s'", util.Type[T]().Name())) + return model.NewErrorTypeFromString(fmt.Sprintf("partial updates are not supported for type '%s'", util.Type[T]().Name())) } if r.data == nil { diff --git a/spine/mocks/Sender.go b/spine/mocks/Sender.go index 058564f9..a47f10e9 100644 --- a/spine/mocks/Sender.go +++ b/spine/mocks/Sender.go @@ -5,8 +5,6 @@ package mocks import ( model "github.com/enbility/eebus-go/spine/model" mock "github.com/stretchr/testify/mock" - - spine "github.com/enbility/eebus-go/spine" ) // Sender is an autogenerated mock type for the Sender type @@ -151,7 +149,7 @@ func (_m *Sender) Request(cmdClassifier model.CmdClassifierType, senderAddress * } // ResultError provides a mock function with given fields: requestHeader, senderAddress, err -func (_m *Sender) ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *spine.ErrorType) error { +func (_m *Sender) ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error { ret := _m.Called(requestHeader, senderAddress, err) if len(ret) == 0 { @@ -159,7 +157,7 @@ func (_m *Sender) ResultError(requestHeader *model.HeaderType, senderAddress *mo } var r0 error - if rf, ok := ret.Get(0).(func(*model.HeaderType, *model.FeatureAddressType, *spine.ErrorType) error); ok { + if rf, ok := ret.Get(0).(func(*model.HeaderType, *model.FeatureAddressType, *model.ErrorType) error); ok { r0 = rf(requestHeader, senderAddress, err) } else { r0 = ret.Error(0) diff --git a/spine/error.go b/spine/model/custom_error.go similarity index 51% rename from spine/error.go rename to spine/model/custom_error.go index ce528b46..e2122626 100644 --- a/spine/error.go +++ b/spine/model/custom_error.go @@ -1,36 +1,35 @@ -package spine +package model import ( "fmt" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" ) type ErrorType struct { - ErrorNumber model.ErrorNumberType - Description *model.DescriptionType + ErrorNumber ErrorNumberType + Description *DescriptionType } -func NewErrorType(errorNumber model.ErrorNumberType, description string) *ErrorType { +func NewErrorType(errorNumber ErrorNumberType, description string) *ErrorType { return &ErrorType{ ErrorNumber: errorNumber, - Description: util.Ptr(model.DescriptionType(description)), + Description: util.Ptr(DescriptionType(description)), } } -func NewErrorTypeFromNumber(errorNumber model.ErrorNumberType) *ErrorType { +func NewErrorTypeFromNumber(errorNumber ErrorNumberType) *ErrorType { return &ErrorType{ ErrorNumber: errorNumber, } } func NewErrorTypeFromString(description string) *ErrorType { - return NewErrorType(model.ErrorNumberTypeGeneralError, description) + return NewErrorType(ErrorNumberTypeGeneralError, description) } -func NewErrorTypeFromResult(result *model.ResultDataType) *ErrorType { - if *result.ErrorNumber == model.ErrorNumberTypeNoError { +func NewErrorTypeFromResult(result *ResultDataType) *ErrorType { + if *result.ErrorNumber == ErrorNumberTypeNoError { return nil } diff --git a/spine/nodemanagement.go b/spine/nodemanagement.go index f6f61e60..85a2c869 100644 --- a/spine/nodemanagement.go +++ b/spine/nodemanagement.go @@ -50,7 +50,7 @@ func (r *NodeManagementImpl) Device() *DeviceLocalImpl { return r.entity.Device() } -func (r *NodeManagementImpl) HandleMessage(message *Message) *ErrorType { +func (r *NodeManagementImpl) HandleMessage(message *Message) *model.ErrorType { switch { case message.Cmd.ResultData != nil: if err := r.processResult(message); err != nil { @@ -60,51 +60,51 @@ func (r *NodeManagementImpl) HandleMessage(message *Message) *ErrorType { case message.Cmd.NodeManagementDetailedDiscoveryData != nil: if err := r.handleMsgDetailedDiscoveryData(message, message.Cmd.NodeManagementDetailedDiscoveryData); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } case message.Cmd.NodeManagementSubscriptionRequestCall != nil: if err := r.handleMsgSubscriptionRequestCall(message, message.Cmd.NodeManagementSubscriptionRequestCall); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } case message.Cmd.NodeManagementSubscriptionDeleteCall != nil: if err := r.handleMsgSubscriptionDeleteCall(message, message.Cmd.NodeManagementSubscriptionDeleteCall); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } case message.Cmd.NodeManagementSubscriptionData != nil: if err := r.handleMsgSubscriptionData(message); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } case message.Cmd.NodeManagementBindingRequestCall != nil: if err := r.handleMsgBindingRequestCall(message, message.Cmd.NodeManagementBindingRequestCall); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } case message.Cmd.NodeManagementBindingDeleteCall != nil: if err := r.handleMsgBindingDeleteCall(message, message.Cmd.NodeManagementBindingDeleteCall); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } case message.Cmd.NodeManagementBindingData != nil: if err := r.handleMsgBindingData(message); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } case message.Cmd.NodeManagementUseCaseData != nil: if err := r.handleMsgUseCaseData(message, message.Cmd.NodeManagementUseCaseData); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } case message.Cmd.NodeManagementDestinationListData != nil: if err := r.handleMsgDestinationListData(message, message.Cmd.NodeManagementDestinationListData); err != nil { - return NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) + return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) } default: - return NewErrorType(model.ErrorNumberTypeCommandNotSupported, fmt.Sprintf("nodemanagement.Handle: Cmd data not implemented: %s", message.Cmd.DataName())) + return model.NewErrorType(model.ErrorNumberTypeCommandNotSupported, fmt.Sprintf("nodemanagement.Handle: Cmd data not implemented: %s", message.Cmd.DataName())) } return nil diff --git a/spine/nodemanagement_destinationlist.go b/spine/nodemanagement_destinationlist.go index e5ba0e03..098a5e49 100644 --- a/spine/nodemanagement_destinationlist.go +++ b/spine/nodemanagement_destinationlist.go @@ -7,8 +7,8 @@ import ( "github.com/enbility/eebus-go/spine/model" ) -func (r *NodeManagementImpl) RequestDestinationListData(remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *ErrorType) { - return nil, NewErrorTypeFromString("Not implemented") +func (r *NodeManagementImpl) RequestDestinationListData(remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *model.ErrorType) { + return nil, model.NewErrorTypeFromString("Not implemented") } func (r *NodeManagementImpl) processReadDestinationListData(featureRemote *FeatureRemoteImpl, requestHeader *model.HeaderType) error { diff --git a/spine/nodemanagement_detaileddiscovery.go b/spine/nodemanagement_detaileddiscovery.go index c9395945..af3909d2 100644 --- a/spine/nodemanagement_detaileddiscovery.go +++ b/spine/nodemanagement_detaileddiscovery.go @@ -8,7 +8,7 @@ import ( ) // request detailed discovery data from a remote device -func (r *NodeManagementImpl) RequestDetailedDiscovery(remoteDeviceSki string, remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *ErrorType) { +func (r *NodeManagementImpl) RequestDetailedDiscovery(remoteDeviceSki string, remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *model.ErrorType) { rfAdress := featureAddressType(NodeManagementFeatureId, EntityAddressType(remoteDeviceAddress, DeviceInformationAddressEntity)) cmd := model.CmdType{ NodeManagementDetailedDiscoveryData: &model.NodeManagementDetailedDiscoveryDataType{}, diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go index d80da232..f2ba0c05 100644 --- a/spine/nodemanagement_usecase.go +++ b/spine/nodemanagement_usecase.go @@ -9,7 +9,7 @@ import ( "github.com/enbility/eebus-go/util" ) -func (r *NodeManagementImpl) RequestUseCaseData(remoteDeviceSki string, remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *ErrorType) { +func (r *NodeManagementImpl) RequestUseCaseData(remoteDeviceSki string, remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *model.ErrorType) { rfAdress := featureAddressType(NodeManagementFeatureId, EntityAddressType(remoteDeviceAddress, DeviceInformationAddressEntity)) cmd := model.CmdType{ NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{}, diff --git a/spine/pending_requests.go b/spine/pending_requests.go index abe10246..328fe6fa 100644 --- a/spine/pending_requests.go +++ b/spine/pending_requests.go @@ -10,15 +10,15 @@ import ( type PendingRequests interface { Add(ski string, counter model.MsgCounterType, maxDelay time.Duration) - SetData(ski string, counter model.MsgCounterType, data any) *ErrorType - SetResult(ski string, counter model.MsgCounterType, errorResult *ErrorType) *ErrorType - GetData(ski string, counter model.MsgCounterType) (any, *ErrorType) - Remove(ski string, counter model.MsgCounterType) *ErrorType + SetData(ski string, counter model.MsgCounterType, data any) *model.ErrorType + SetResult(ski string, counter model.MsgCounterType, errorResult *model.ErrorType) *model.ErrorType + GetData(ski string, counter model.MsgCounterType) (any, *model.ErrorType) + Remove(ski string, counter model.MsgCounterType) *model.ErrorType } type dataErrorPair struct { data any - errorResult *ErrorType + errorResult *model.ErrorType } type request struct { @@ -30,7 +30,7 @@ type request struct { func (r *request) setTimeoutResult() { if len(r.response) == 0 { - errorResult := NewErrorType(model.ErrorNumberTypeTimeout, fmt.Sprintf("the request with the message counter '%s' timed out", r.counter.String())) + errorResult := model.NewErrorType(model.ErrorNumberTypeTimeout, fmt.Sprintf("the request with the message counter '%s' timed out", r.counter.String())) r.response <- &dataErrorPair{data: nil, errorResult: errorResult} } } @@ -57,15 +57,15 @@ func (r *PendingRequestsImpl) Add(ski string, counter model.MsgCounterType, maxD r.requestMap.Store(r.mapKey(ski, counter), newRequest) } -func (r *PendingRequestsImpl) SetData(ski string, counter model.MsgCounterType, data any) *ErrorType { +func (r *PendingRequestsImpl) SetData(ski string, counter model.MsgCounterType, data any) *model.ErrorType { return r.setResponse(ski, counter, data, nil) } -func (r *PendingRequestsImpl) SetResult(ski string, counter model.MsgCounterType, errorResult *ErrorType) *ErrorType { +func (r *PendingRequestsImpl) SetResult(ski string, counter model.MsgCounterType, errorResult *model.ErrorType) *model.ErrorType { return r.setResponse(ski, counter, nil, errorResult) } -func (r *PendingRequestsImpl) GetData(ski string, counter model.MsgCounterType) (any, *ErrorType) { +func (r *PendingRequestsImpl) GetData(ski string, counter model.MsgCounterType) (any, *model.ErrorType) { request, err := r.getRequest(ski, counter) if err != nil { return nil, err @@ -77,7 +77,7 @@ func (r *PendingRequestsImpl) GetData(ski string, counter model.MsgCounterType) return data.data, data.errorResult } -func (r *PendingRequestsImpl) Remove(ski string, counter model.MsgCounterType) *ErrorType { +func (r *PendingRequestsImpl) Remove(ski string, counter model.MsgCounterType) *model.ErrorType { request, err := r.getRequest(ski, counter) if err != nil { return err @@ -95,23 +95,23 @@ func (r *PendingRequestsImpl) removeRequest(request *request) { r.requestMap.Delete(r.mapKey(request.ski, request.counter)) } -func (r *PendingRequestsImpl) getRequest(ski string, counter model.MsgCounterType) (*request, *ErrorType) { +func (r *PendingRequestsImpl) getRequest(ski string, counter model.MsgCounterType) (*request, *model.ErrorType) { rq, exists := r.requestMap.Load(r.mapKey(ski, counter)) if !exists { - return nil, NewErrorTypeFromString(fmt.Sprintf("No pending request with message counter '%s' found", counter.String())) + return nil, model.NewErrorTypeFromString(fmt.Sprintf("No pending request with message counter '%s' found", counter.String())) } return rq.(*request), nil } -func (r *PendingRequestsImpl) setResponse(ski string, counter model.MsgCounterType, data any, errorResult *ErrorType) *ErrorType { +func (r *PendingRequestsImpl) setResponse(ski string, counter model.MsgCounterType, data any, errorResult *model.ErrorType) *model.ErrorType { request, err := r.getRequest(ski, counter) if err != nil { return err } if len(request.response) > 0 { - return NewErrorTypeFromString(fmt.Sprintf("the Data or Result for the request (MsgCounter: %s) was already set!", &counter)) + return model.NewErrorTypeFromString(fmt.Sprintf("the Data or Result for the request (MsgCounter: %s) was already set!", &counter)) } request.countdown.Stop() diff --git a/spine/pending_requests_test.go b/spine/pending_requests_test.go index b148b683..b77ece98 100644 --- a/spine/pending_requests_test.go +++ b/spine/pending_requests_test.go @@ -80,21 +80,21 @@ func (suite *PendingRequestsTestSuite) TestPendingRequests_SetData_SetData() { func (suite *PendingRequestsTestSuite) TestPendingRequests_SetResult() { // Act - err := suite.sut.SetResult(suite.ski, suite.counter, NewErrorTypeFromString("unknown error")) + err := suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorTypeFromString("unknown error")) assert.Nil(suite.T(), err) } func (suite *PendingRequestsTestSuite) TestPendingRequests_SetResult_SetResult() { - _ = suite.sut.SetResult(suite.ski, suite.counter, NewErrorTypeFromString("unknown error")) + _ = suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorTypeFromString("unknown error")) // Act - err := suite.sut.SetResult(suite.ski, suite.counter, NewErrorTypeFromString("unknown error")) + err := suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorTypeFromString("unknown error")) assert.NotNil(suite.T(), err) } func (suite *PendingRequestsTestSuite) TestPendingRequests_SetData_SetResult() { _ = suite.sut.SetData(suite.ski, suite.counter, 1) // Act - err := suite.sut.SetResult(suite.ski, suite.counter, NewErrorTypeFromString("unknown error")) + err := suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorTypeFromString("unknown error")) assert.NotNil(suite.T(), err) } @@ -112,7 +112,7 @@ func (suite *PendingRequestsTestSuite) TestPendingRequests_SetData_GetData() { func (suite *PendingRequestsTestSuite) TestPendingRequests_SetResult_GetData() { errNo := model.ErrorNumberTypeTimeout errDesc := "Timeout occurred" - _ = suite.sut.SetResult(suite.ski, suite.counter, NewErrorType(errNo, errDesc)) + _ = suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorType(errNo, errDesc)) // Act result, err := suite.sut.GetData(suite.ski, suite.counter) diff --git a/spine/send.go b/spine/send.go index 936cbe3b..843e8b28 100644 --- a/spine/send.go +++ b/spine/send.go @@ -24,7 +24,7 @@ type Sender interface { // Sends a result cmd with no error to indicate that a message was processed successfully ResultSuccess(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType) error // Sends a result cmd with error information to indicate that a message processing failed - ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *ErrorType) error + ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error // Sends a reply cmd to response to a read cmd Reply(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, cmd model.CmdType) error // Sends a call cmd with a subscription request @@ -123,12 +123,12 @@ func (c *SenderImpl) ResultSuccess(requestHeader *model.HeaderType, senderAddres return c.result(requestHeader, senderAddress, nil) } -func (c *SenderImpl) ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *ErrorType) error { +func (c *SenderImpl) ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error { return c.result(requestHeader, senderAddress, err) } // sends a result for a request -func (c *SenderImpl) result(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *ErrorType) error { +func (c *SenderImpl) result(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error { cmdClassifier := model.CmdClassifierTypeResult addressSource := *requestHeader.AddressDestination From 3349974754617ba100b7308ddf66ab7da2fc43fb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:10:55 +0100 Subject: [PATCH 118/240] Use spine package for tests --- spine/binding_manager_test.go | 23 +++++++++++----------- spine/device_local_test.go | 29 ++++++++++++++-------------- spine/device_remote_test.go | 21 ++++++++++---------- spine/entity_local_test.go | 9 ++++----- spine/feature_local_test.go | 19 +++++++++--------- spine/feature_remote_test.go | 13 ++++++------- spine/heartbeat_manager_test.go | 23 +++++++++++----------- spine/nodemanagement_test.go | 31 +++++++++++++++--------------- spine/send_test.go | 21 +++++--------------- spine/subscription_manager_test.go | 23 +++++++++++----------- spine/usecase_test.go | 13 ++++++------- 11 files changed, 102 insertions(+), 123 deletions(-) diff --git a/spine/binding_manager_test.go b/spine/binding_manager_test.go index c1195c5f..b741bf48 100644 --- a/spine/binding_manager_test.go +++ b/spine/binding_manager_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" @@ -18,35 +17,35 @@ func TestBindingManagerSuite(t *testing.T) { type BindingManagerSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl - remoteDevice *spine.DeviceRemoteImpl - sut spine.BindingManager + localDevice *DeviceLocalImpl + remoteDevice *DeviceRemoteImpl + sut BindingManager } func (suite *BindingManagerSuite) WriteSpineMessage([]byte) {} func (suite *BindingManagerSuite) SetupSuite() { - suite.localDevice = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + suite.localDevice = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) ski := "test" - sender := spine.NewSender(suite) - suite.remoteDevice = spine.NewDeviceRemoteImpl(suite.localDevice, ski, sender) + sender := NewSender(suite) + suite.remoteDevice = NewDeviceRemoteImpl(suite.localDevice, ski, sender) suite.localDevice.AddRemoteDevice(ski, suite) - suite.sut = spine.NewBindingManager(suite.localDevice) + suite.sut = NewBindingManager(suite.localDevice) } func (suite *BindingManagerSuite) Test_Bindings() { - entity := spine.NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) + entity := NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) suite.localDevice.AddEntity(entity) localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - remoteEntity := spine.NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + remoteEntity := NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) suite.remoteDevice.AddEntity(remoteEntity) - remoteFeature := spine.NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) remoteFeature.Address().Device = util.Ptr(model.AddressDeviceType("remoteDevice")) remoteEntity.AddFeature(remoteFeature) diff --git a/spine/device_local_test.go b/spine/device_local_test.go index fcc95d13..a7b1960a 100644 --- a/spine/device_local_test.go +++ b/spine/device_local_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" @@ -19,12 +18,12 @@ type DeviceLocalTestSuite struct { suite.Suite } -var _ spine.SpineDataConnection = (*DeviceLocalTestSuite)(nil) +var _ SpineDataConnection = (*DeviceLocalTestSuite)(nil) func (d *DeviceLocalTestSuite) WriteSpineMessage([]byte) {} func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() { - sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) ski := "test" sut.AddRemoteDevice(ski, d) @@ -38,13 +37,13 @@ func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() { } func (d *DeviceLocalTestSuite) Test_RemoteDevice() { - sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + localEntity := NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) sut.AddEntity(localEntity) - f := spine.NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + f := NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + f = NewFeatureLocalImpl(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) localEntity.AddFeature(f) ski := "test" @@ -90,8 +89,8 @@ func (d *DeviceLocalTestSuite) Test_RemoteDevice() { } func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() { - sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + localEntity := NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) sut.AddEntity(localEntity) ski := "test" @@ -141,15 +140,15 @@ func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() { } func (d *DeviceLocalTestSuite) Test_ProcessCmd() { - sut := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + localEntity := NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) sut.AddEntity(localEntity) - f := spine.NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + f := NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + f = NewFeatureLocalImpl(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(3, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + f = NewFeatureLocalImpl(3, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) localEntity.AddFeature(f) ski := "test" diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index 9c05f95d..d3256dc1 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -17,8 +16,8 @@ func TestDeviceRemoteSuite(t *testing.T) { type DeviceRemoteSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl - remoteDevice *spine.DeviceRemoteImpl + localDevice *DeviceLocalImpl + remoteDevice *DeviceRemoteImpl } func (s *DeviceRemoteSuite) WriteSpineMessage([]byte) {} @@ -26,16 +25,16 @@ func (s *DeviceRemoteSuite) WriteSpineMessage([]byte) {} func (s *DeviceRemoteSuite) SetupSuite() {} func (s *DeviceRemoteSuite) BeforeTest(suiteName, testName string) { - s.localDevice = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + s.localDevice = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) ski := "test" - sender := spine.NewSender(s) - s.remoteDevice = spine.NewDeviceRemoteImpl(s.localDevice, ski, sender) + sender := NewSender(s) + s.remoteDevice = NewDeviceRemoteImpl(s.localDevice, ski, sender) s.localDevice.AddRemoteDevice(ski, s) - entity := spine.NewEntityRemoteImpl(s.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + entity := NewEntityRemoteImpl(s.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) - feature := spine.NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + feature := NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) entity.AddFeature(feature) s.remoteDevice.AddEntity(entity) @@ -174,7 +173,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { entity := s.remoteDevice.Entity([]model.AddressEntityType{1}) assert.NotNil(s.T(), entity) - feature := spine.NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + feature := NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) entity.AddFeature(feature) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( @@ -185,7 +184,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { ) assert.Equal(s.T(), false, result) - feature = spine.NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + feature = NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) entity.AddFeature(feature) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( diff --git a/spine/entity_local_test.go b/spine/entity_local_test.go index 11c40776..b48668ad 100644 --- a/spine/entity_local_test.go +++ b/spine/entity_local_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -19,11 +18,11 @@ type EntityLocalTestSuite struct { } func (suite *EntityLocalTestSuite) Test_Entity() { - device := spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - entity := spine.NewEntityLocalImpl(device, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + device := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + entity := NewEntityLocalImpl(device, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) device.AddEntity(entity) - f := spine.NewFeatureLocalImpl(1, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + f := NewFeatureLocalImpl(1, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) entity.AddFeature(f) assert.Equal(suite.T(), 1, len(entity.Features())) diff --git a/spine/feature_local_test.go b/spine/feature_local_test.go index df2fc4b6..ea335866 100644 --- a/spine/feature_local_test.go +++ b/spine/feature_local_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/mocks" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -23,8 +22,8 @@ type DeviceClassificationTestSuite struct { function model.FunctionType featureType model.FeatureTypeType msgCounter model.MsgCounterType - remoteFeature *spine.FeatureRemoteImpl - sut *spine.FeatureLocalImpl + remoteFeature *FeatureRemoteImpl + sut *FeatureLocalImpl } func (suite *DeviceClassificationTestSuite) SetupSuite() { @@ -52,7 +51,7 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Rep SerialNumber: util.Ptr(model.DeviceClassificationStringType("serial number")), } - replyMsg := spine.Message{ + replyMsg := Message{ Cmd: model.CmdType{ DeviceClassificationManufacturerData: manufacturerData, }, @@ -94,7 +93,7 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Err msgCounter, err := suite.sut.RequestData(suite.function, nil, nil, suite.remoteFeature) assert.Nil(suite.T(), err) - replyMsg := spine.Message{ + replyMsg := Message{ Cmd: model.CmdType{ ResultData: &model.ResultDataType{ ErrorNumber: util.Ptr(model.ErrorNumberType(errorNumber)), @@ -126,11 +125,11 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Err assert.Equal(suite.T(), errorDescription, string(*err.Description)) } -func createLocalDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType) *spine.FeatureLocalImpl { - localDevice := spine.NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) +func createLocalDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType) *FeatureLocalImpl { + localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + localEntity := NewEntityLocalImpl(localDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) localDevice.AddEntity(localEntity) - localFeature := spine.NewFeatureLocalImpl(localEntity.NextFeatureId(), localEntity, featureType, role) + localFeature := NewFeatureLocalImpl(localEntity.NextFeatureId(), localEntity, featureType, role) localEntity.AddFeature(localFeature) return localFeature } diff --git a/spine/feature_remote_test.go b/spine/feature_remote_test.go index a1bbdfd0..409cb8fc 100644 --- a/spine/feature_remote_test.go +++ b/spine/feature_remote_test.go @@ -1,20 +1,19 @@ -package spine_test +package spine import ( "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" ) -func createRemoteDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType, sender spine.Sender) *spine.FeatureRemoteImpl { - localDevice := spine.NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) +func createRemoteDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType, sender Sender) *FeatureRemoteImpl { + localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - remoteDevice := spine.NewDeviceRemoteImpl(localDevice, "ski", sender) + remoteDevice := NewDeviceRemoteImpl(localDevice, "ski", sender) // remoteDevice.address = util.Ptr(model.AddressDeviceType("Address")) - remoteEntity := spine.NewEntityRemoteImpl(remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) + remoteEntity := NewEntityRemoteImpl(remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) remoteDevice.AddEntity(remoteEntity) - remoteFeature := spine.NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, role) + remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, role) remoteEntity.AddFeature(remoteFeature) return remoteFeature } diff --git a/spine/heartbeat_manager_test.go b/spine/heartbeat_manager_test.go index 5afc1eb8..05c58644 100644 --- a/spine/heartbeat_manager_test.go +++ b/spine/heartbeat_manager_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" @@ -18,19 +17,19 @@ func TestHeartbeatManagerSuite(t *testing.T) { type HeartBeatManagerSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl - remoteDevice *spine.DeviceRemoteImpl - sut spine.HeartbeatManager + localDevice *DeviceLocalImpl + remoteDevice *DeviceRemoteImpl + sut HeartbeatManager } func (suite *HeartBeatManagerSuite) WriteSpineMessage([]byte) {} func (suite *HeartBeatManagerSuite) SetupSuite() { - suite.localDevice = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + suite.localDevice = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) ski := "test" - sender := spine.NewSender(suite) - suite.remoteDevice = spine.NewDeviceRemoteImpl(suite.localDevice, ski, sender) + sender := NewSender(suite) + suite.remoteDevice = NewDeviceRemoteImpl(suite.localDevice, ski, sender) suite.localDevice.AddRemoteDevice(ski, suite) @@ -43,16 +42,16 @@ func (suite *HeartBeatManagerSuite) Test_HeartbeatFailure() { } func (suite *HeartBeatManagerSuite) Test_HeartbeatSuccess() { - entity := spine.NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) + entity := NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) suite.localDevice.AddEntity(entity) localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) entity.AddFeature(localFeature) - remoteEntity := spine.NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + remoteEntity := NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) suite.remoteDevice.AddEntity(remoteEntity) - remoteFeature := spine.NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) remoteEntity.AddFeature(remoteFeature) subscrRequest := &model.SubscriptionManagementRequestCallType{ @@ -63,7 +62,7 @@ func (suite *HeartBeatManagerSuite) Test_HeartbeatSuccess() { datagram := model.DatagramType{ Header: model.HeaderType{ - SpecificationVersion: &spine.SpecificationVersion, + SpecificationVersion: &SpecificationVersion, AddressSource: &model.FeatureAddressType{ Device: suite.remoteDevice.Address(), Entity: []model.AddressEntityType{0}, diff --git a/spine/nodemanagement_test.go b/spine/nodemanagement_test.go index afd0d773..dbe33736 100644 --- a/spine/nodemanagement_test.go +++ b/spine/nodemanagement_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "reflect" "testing" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/mocks" "github.com/enbility/eebus-go/spine/model" "github.com/stretchr/testify/assert" @@ -27,22 +26,22 @@ func TestNodemanagement_BindingCalls(t *testing.T) { assert.True(t, reflect.DeepEqual(cmd.NodeManagementBindingData.BindingEntry[0].ServerAddress, serverFeature.Address())) }).Return(nil).Once() - requestMsg := spine.Message{ + requestMsg := Message{ Cmd: model.CmdType{ - NodeManagementBindingRequestCall: spine.NewNodeManagementBindingRequestCallType( + NodeManagementBindingRequestCall: NewNodeManagementBindingRequestCallType( clientFeature.Address(), serverFeature.Address(), featureType), }, CmdClassifier: model.CmdClassifierTypeCall, FeatureRemote: clientFeature, } - sut := spine.NewNodeManagementImpl(0, serverFeature.Entity()) + sut := NewNodeManagementImpl(0, serverFeature.Entity()) // Act err := sut.HandleMessage(&requestMsg) if assert.Nil(t, err) { - dataMsg := spine.Message{ + dataMsg := Message{ Cmd: model.CmdType{ NodeManagementBindingData: &model.NodeManagementBindingDataType{}, }, @@ -58,9 +57,9 @@ func TestNodemanagement_BindingCalls(t *testing.T) { assert.Equal(t, 0, len(cmd.NodeManagementBindingData.BindingEntry)) }).Return(nil).Once() - deleteMsg := spine.Message{ + deleteMsg := Message{ Cmd: model.CmdType{ - NodeManagementBindingDeleteCall: spine.NewNodeManagementBindingDeleteCallType( + NodeManagementBindingDeleteCall: NewNodeManagementBindingDeleteCallType( clientFeature.Address(), serverFeature.Address()), }, CmdClassifier: model.CmdClassifierTypeCall, @@ -71,7 +70,7 @@ func TestNodemanagement_BindingCalls(t *testing.T) { err = sut.HandleMessage(&deleteMsg) if assert.Nil(t, err) { - dataMsg := spine.Message{ + dataMsg := Message{ Cmd: model.CmdType{ NodeManagementBindingData: &model.NodeManagementBindingDataType{}, }, @@ -99,22 +98,22 @@ func TestNodemanagement_SubscriptionCalls(t *testing.T) { assert.True(t, reflect.DeepEqual(cmd.NodeManagementSubscriptionData.SubscriptionEntry[0].ServerAddress, serverFeature.Address())) }).Return(nil).Once() - requestMsg := spine.Message{ + requestMsg := Message{ Cmd: model.CmdType{ - NodeManagementSubscriptionRequestCall: spine.NewNodeManagementSubscriptionRequestCallType( + NodeManagementSubscriptionRequestCall: NewNodeManagementSubscriptionRequestCallType( clientFeature.Address(), serverFeature.Address(), featureType), }, CmdClassifier: model.CmdClassifierTypeCall, FeatureRemote: clientFeature, } - sut := spine.NewNodeManagementImpl(0, serverFeature.Entity()) + sut := NewNodeManagementImpl(0, serverFeature.Entity()) // Act err := sut.HandleMessage(&requestMsg) if assert.Nil(t, err) { - dataMsg := spine.Message{ + dataMsg := Message{ Cmd: model.CmdType{ NodeManagementSubscriptionData: &model.NodeManagementSubscriptionDataType{}, }, @@ -130,9 +129,9 @@ func TestNodemanagement_SubscriptionCalls(t *testing.T) { assert.Equal(t, 0, len(cmd.NodeManagementSubscriptionData.SubscriptionEntry)) }).Return(nil).Once() - deleteMsg := spine.Message{ + deleteMsg := Message{ Cmd: model.CmdType{ - NodeManagementSubscriptionDeleteCall: spine.NewNodeManagementSubscriptionDeleteCallType( + NodeManagementSubscriptionDeleteCall: NewNodeManagementSubscriptionDeleteCallType( clientFeature.Address(), serverFeature.Address(), featureType), }, CmdClassifier: model.CmdClassifierTypeCall, @@ -143,7 +142,7 @@ func TestNodemanagement_SubscriptionCalls(t *testing.T) { err = sut.HandleMessage(&deleteMsg) if assert.Nil(t, err) { - dataMsg := spine.Message{ + dataMsg := Message{ Cmd: model.CmdType{ NodeManagementSubscriptionData: &model.NodeManagementSubscriptionDataType{}, }, diff --git a/spine/send_test.go b/spine/send_test.go index 6310b5ad..04989353 100644 --- a/spine/send_test.go +++ b/spine/send_test.go @@ -1,21 +1,20 @@ -package spine_test +package spine import ( "encoding/json" "testing" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestSender_Notify_MsgCounter(t *testing.T) { - temp := &spine.WriteMessageHandler{} - sut := spine.NewSender(temp) + temp := &WriteMessageHandler{} + sut := NewSender(temp) - senderAddress := featureAddressType(1, spine.NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, spine.NewEntityAddressType("destination", []uint{1})) + senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) + destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) cmd := model.CmdType{ ResultData: &model.ResultDataType{ErrorNumber: util.Ptr(model.ErrorNumberType(model.ErrorNumberTypeNoError))}, } @@ -33,13 +32,3 @@ func TestSender_Notify_MsgCounter(t *testing.T) { assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) } - -func featureAddressType(id uint, entityAddress *model.EntityAddressType) *model.FeatureAddressType { - res := model.FeatureAddressType{ - Device: entityAddress.Device, - Entity: entityAddress.Entity, - Feature: util.Ptr(model.AddressFeatureType(id)), - } - - return &res -} diff --git a/spine/subscription_manager_test.go b/spine/subscription_manager_test.go index 8561f3a9..c3cdd9ab 100644 --- a/spine/subscription_manager_test.go +++ b/spine/subscription_manager_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" @@ -18,35 +17,35 @@ func TestSubscriptionManagerSuite(t *testing.T) { type SubscriptionManagerSuite struct { suite.Suite - localDevice *spine.DeviceLocalImpl - remoteDevice *spine.DeviceRemoteImpl - sut spine.SubscriptionManager + localDevice *DeviceLocalImpl + remoteDevice *DeviceRemoteImpl + sut SubscriptionManager } func (suite *SubscriptionManagerSuite) WriteSpineMessage([]byte) {} func (suite *SubscriptionManagerSuite) SetupSuite() { - suite.localDevice = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + suite.localDevice = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) ski := "test" - sender := spine.NewSender(suite) - suite.remoteDevice = spine.NewDeviceRemoteImpl(suite.localDevice, ski, sender) + sender := NewSender(suite) + suite.remoteDevice = NewDeviceRemoteImpl(suite.localDevice, ski, sender) suite.localDevice.AddRemoteDevice(ski, suite) - suite.sut = spine.NewSubscriptionManager(suite.localDevice) + suite.sut = NewSubscriptionManager(suite.localDevice) } func (suite *SubscriptionManagerSuite) Test_Subscriptions() { - entity := spine.NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) + entity := NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) suite.localDevice.AddEntity(entity) localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - remoteEntity := spine.NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + remoteEntity := NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) suite.remoteDevice.AddEntity(remoteEntity) - remoteFeature := spine.NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) remoteFeature.Address().Device = util.Ptr(model.AddressDeviceType("remoteDevice")) remoteEntity.AddFeature(remoteFeature) diff --git a/spine/usecase_test.go b/spine/usecase_test.go index f6273972..f2354435 100644 --- a/spine/usecase_test.go +++ b/spine/usecase_test.go @@ -1,10 +1,9 @@ -package spine_test +package spine import ( "testing" "time" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -17,19 +16,19 @@ func TestUsecaseSuite(t *testing.T) { type UsecaseSuite struct { suite.Suite - device *spine.DeviceLocalImpl - entity *spine.EntityLocalImpl + device *DeviceLocalImpl + entity *EntityLocalImpl } func (s *UsecaseSuite) BeforeTest(suiteName, testName string) { - s.device = spine.NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - s.entity = spine.NewEntityLocalImpl(s.device, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + s.device = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + s.entity = NewEntityLocalImpl(s.device, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) s.device.AddEntity(s.entity) } func (s *UsecaseSuite) Test_UseCase() { - uc := spine.NewUseCase( + uc := NewUseCase( s.entity, model.UseCaseNameTypeControlOfBattery, model.SpecificationVersionType("1.0.0"), From 503b1ab2e1edb8975b4e8a22b8e612879d30d282 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:17:21 +0100 Subject: [PATCH 119/240] Move spine interfaces into api file --- spine/api.go | 184 ++++++++++++++++++++++++++++++++++ spine/binding_manager.go | 9 -- spine/device_local.go | 6 -- spine/events.go | 4 - spine/feature.go | 6 -- spine/feature_local.go | 49 --------- spine/function_data.go | 6 -- spine/function_data_cmd.go | 8 -- spine/heartbeat_manager.go | 7 -- spine/pending_requests.go | 8 -- spine/send.go | 28 ------ spine/subscription_manager.go | 9 -- spine/types.go | 21 ---- 13 files changed, 184 insertions(+), 161 deletions(-) create mode 100644 spine/api.go delete mode 100644 spine/types.go diff --git a/spine/api.go b/spine/api.go new file mode 100644 index 00000000..90060bb8 --- /dev/null +++ b/spine/api.go @@ -0,0 +1,184 @@ +package spine + +import ( + "time" + + "github.com/enbility/eebus-go/spine/model" +) + +type EventHandler interface { + HandleEvent(EventPayload) +} + +/* Device */ + +// implemented by spine.DeviceLocalImpl and used by shipConnection +type DeviceLocalConnection interface { + RemoveRemoteDeviceConnection(ski string) + AddRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing +} + +/* Feature */ + +type Feature interface { + Address() *model.FeatureAddressType + Type() model.FeatureTypeType + Role() model.RoleType +} + +type FeatureLocal interface { + Feature + Data(function model.FunctionType) any + SetData(function model.FunctionType, data any) + AddResultHandler(handler FeatureResult) + AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg ResultMessage)) + Information() *model.NodeManagementDetailedDiscoveryFeatureInformationType + AddFunctionType(function model.FunctionType, read, write bool) + RequestData( + function model.FunctionType, + selector any, + elements any, + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) + RequestDataBySenderAddress( + cmd model.CmdType, + sender Sender, + destinationSki string, + destinationAddress *model.FeatureAddressType, + maxDelay time.Duration) (*model.MsgCounterType, *model.ErrorType) + FetchRequestData( + msgCounter model.MsgCounterType, + destination *FeatureRemoteImpl) (any, *model.ErrorType) + RequestAndFetchData( + function model.FunctionType, + selector any, + elements any, + destination *FeatureRemoteImpl) (any, *model.ErrorType) + Subscribe(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) + // SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed + Bind(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) + // BindAndWait(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) *ErrorType + NotifyData( + function model.FunctionType, + deleteSelector, partialSelector any, + partialWithoutSelector bool, + deleteElements any, + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) + WriteData( + function model.FunctionType, + deleteSelector, partialSelector any, + deleteElements any, + destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) + HandleMessage(message *Message) *model.ErrorType +} + +type FeatureResult interface { + HandleResult(ResultMessage) +} + +/* Functions */ + +type FunctionDataCmd interface { + FunctionData + ReadCmdType(partialSelector any, elements any) model.CmdType + ReplyCmdType(partial bool) model.CmdType + NotifyCmdType(deleteSelector, partialSelector any, partialWithoutSelector bool, deleteElements any) model.CmdType + WriteCmdType(deleteSelector, partialSelector any, deleteElements any) model.CmdType +} + +type FunctionData interface { + Function() model.FunctionType + DataAny() any + UpdateDataAny(data any, filterPartial *model.FilterType, filterDelete *model.FilterType) +} + +/* Sender */ + +type ComControl interface { + // This must be connected to the correct remote device !! + SendSpineMessage(datagram model.DatagramType) error +} + +//go:generate mockery --name=Sender + +type Sender interface { + // Sends a read cmd to request some data + Request(cmdClassifier model.CmdClassifierType, senderAddress, destinationAddress *model.FeatureAddressType, ackRequest bool, cmd []model.CmdType) (*model.MsgCounterType, error) + // Sends a result cmd with no error to indicate that a message was processed successfully + ResultSuccess(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType) error + // Sends a result cmd with error information to indicate that a message processing failed + ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error + // Sends a reply cmd to response to a read cmd + Reply(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, cmd model.CmdType) error + // Sends a call cmd with a subscription request + Subscribe(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) + // Sends a call cmd with a binding request + Bind(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) + // Sends a notify cmd to indicate that a subscribed feature changed + Notify(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) + // Sends a write cmd, setting properties of remote features + Write(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) + // return the datagram for a given msgCounter (only availbe for Notify messasges!), error if not found + DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) +} + +/* PendingRequests */ + +type PendingRequests interface { + Add(ski string, counter model.MsgCounterType, maxDelay time.Duration) + SetData(ski string, counter model.MsgCounterType, data any) *model.ErrorType + SetResult(ski string, counter model.MsgCounterType, errorResult *model.ErrorType) *model.ErrorType + GetData(ski string, counter model.MsgCounterType) (any, *model.ErrorType) + Remove(ski string, counter model.MsgCounterType) *model.ErrorType +} + +/* Bindings */ + +// implemented by spine.BindingManagerImpl +type BindingManager interface { + AddBinding(remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error + RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error + RemoveBindingsForDevice(remoteDevice *DeviceRemoteImpl) + RemoveBindingsForEntity(remoteEntity *EntityRemoteImpl) + Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry + BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry +} + +/* Subscription Manager */ + +type SubscriptionManager interface { + AddSubscription(remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error + RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error + RemoveSubscriptionsForDevice(remoteDevice *DeviceRemoteImpl) + RemoveSubscriptionsForEntity(remoteEntity *EntityRemoteImpl) + Subscriptions(remoteDevice *DeviceRemoteImpl) []*SubscriptionEntry + SubscriptionsOnFeature(featureAddress model.FeatureAddressType) []*SubscriptionEntry +} + +/* Heartbeats */ + +type HeartbeatManager interface { + IsHeartbeatRunning() bool + UpdateHeartbeatOnSubscriptions() + StartHeartbeat() error + StopHeartbeat() +} + +/* Processing */ + +//go:generate mockery --name=SpineDataProcessing + +// Used to pass an incoming SPINE message from a SHIP connection to the proper DeviceRemoteImpl +// +// Implemented by DeviceRemoteImpl, used by ShipConnection +type SpineDataProcessing interface { + HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) +} + +//go:generate mockery --name=SpineDataConnection + +// Used to pass an outgoing SPINE message from a DeviceLocalImpl to the SHIP connection +// +// Implemented by ShipConnection, used by DeviceLocalImpl +type SpineDataConnection interface { + WriteSpineMessage(message []byte) +} diff --git a/spine/binding_manager.go b/spine/binding_manager.go index 0512c234..f3c0c4c4 100644 --- a/spine/binding_manager.go +++ b/spine/binding_manager.go @@ -12,15 +12,6 @@ import ( "github.com/enbility/eebus-go/util" ) -type BindingManager interface { - AddBinding(remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error - RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error - RemoveBindingsForDevice(remoteDevice *DeviceRemoteImpl) - RemoveBindingsForEntity(remoteEntity *EntityRemoteImpl) - Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry - BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry -} - type BindingEntry struct { id uint64 serverFeature FeatureLocal diff --git a/spine/device_local.go b/spine/device_local.go index fe93ff65..404bfa4a 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -12,12 +12,6 @@ import ( "github.com/enbility/eebus-go/util" ) -// implemented by spine.DeviceLocalImpl and used by shipConnection -type DeviceLocalConnection interface { - RemoveRemoteDeviceConnection(ski string) - AddRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing -} - type DeviceLocalImpl struct { *DeviceImpl entities []*EntityLocalImpl diff --git a/spine/events.go b/spine/events.go index e01dd65a..ab1de0ef 100644 --- a/spine/events.go +++ b/spine/events.go @@ -44,10 +44,6 @@ type EventPayload struct { Data any } -type EventHandler interface { - HandleEvent(EventPayload) -} - type eventHandlerItem struct { Level EventHandlerLevel Handler EventHandler diff --git a/spine/feature.go b/spine/feature.go index e359daa9..7b553df9 100644 --- a/spine/feature.go +++ b/spine/feature.go @@ -7,12 +7,6 @@ import ( "github.com/enbility/eebus-go/util" ) -type Feature interface { - Address() *model.FeatureAddressType - Type() model.FeatureTypeType - Role() model.RoleType -} - type FeatureImpl struct { address *model.FeatureAddressType ftype model.FeatureTypeType diff --git a/spine/feature_local.go b/spine/feature_local.go index d23e3278..c5423588 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -10,55 +10,6 @@ import ( "github.com/enbility/eebus-go/util" ) -type FeatureLocal interface { - Feature - Data(function model.FunctionType) any - SetData(function model.FunctionType, data any) - AddResultHandler(handler FeatureResult) - AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg ResultMessage)) - Information() *model.NodeManagementDetailedDiscoveryFeatureInformationType - AddFunctionType(function model.FunctionType, read, write bool) - RequestData( - function model.FunctionType, - selector any, - elements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) - RequestDataBySenderAddress( - cmd model.CmdType, - sender Sender, - destinationSki string, - destinationAddress *model.FeatureAddressType, - maxDelay time.Duration) (*model.MsgCounterType, *model.ErrorType) - FetchRequestData( - msgCounter model.MsgCounterType, - destination *FeatureRemoteImpl) (any, *model.ErrorType) - RequestAndFetchData( - function model.FunctionType, - selector any, - elements any, - destination *FeatureRemoteImpl) (any, *model.ErrorType) - Subscribe(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) - // SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed - Bind(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) - // BindAndWait(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) *ErrorType - NotifyData( - function model.FunctionType, - deleteSelector, partialSelector any, - partialWithoutSelector bool, - deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) - WriteData( - function model.FunctionType, - deleteSelector, partialSelector any, - deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) - HandleMessage(message *Message) *model.ErrorType -} - -type FeatureResult interface { - HandleResult(ResultMessage) -} - var _ FeatureLocal = (*FeatureLocalImpl)(nil) type FeatureLocalImpl struct { diff --git a/spine/function_data.go b/spine/function_data.go index 9820d9ed..d79f3891 100644 --- a/spine/function_data.go +++ b/spine/function_data.go @@ -9,12 +9,6 @@ import ( "github.com/enbility/eebus-go/util" ) -type FunctionData interface { - Function() model.FunctionType - DataAny() any - UpdateDataAny(data any, filterPartial *model.FilterType, filterDelete *model.FilterType) -} - var _ FunctionData = (*FunctionDataImpl[int])(nil) type FunctionDataImpl[T any] struct { diff --git a/spine/function_data_cmd.go b/spine/function_data_cmd.go index aa2a7509..ef3d2f8b 100644 --- a/spine/function_data_cmd.go +++ b/spine/function_data_cmd.go @@ -5,14 +5,6 @@ import ( "github.com/enbility/eebus-go/util" ) -type FunctionDataCmd interface { - FunctionData - ReadCmdType(partialSelector any, elements any) model.CmdType - ReplyCmdType(partial bool) model.CmdType - NotifyCmdType(deleteSelector, partialSelector any, partialWithoutSelector bool, deleteElements any) model.CmdType - WriteCmdType(deleteSelector, partialSelector any, deleteElements any) model.CmdType -} - var _ FunctionDataCmd = (*FunctionDataCmdImpl[int])(nil) type FunctionDataCmdImpl[T any] struct { diff --git a/spine/heartbeat_manager.go b/spine/heartbeat_manager.go index ed5219a1..ed74719f 100644 --- a/spine/heartbeat_manager.go +++ b/spine/heartbeat_manager.go @@ -9,13 +9,6 @@ import ( "github.com/enbility/eebus-go/spine/model" ) -type HeartbeatManager interface { - IsHeartbeatRunning() bool - UpdateHeartbeatOnSubscriptions() - StartHeartbeat() error - StopHeartbeat() -} - type HeartbeatManagerImpl struct { localDevice *DeviceLocalImpl localEntity *EntityLocalImpl diff --git a/spine/pending_requests.go b/spine/pending_requests.go index 328fe6fa..aa3e96e8 100644 --- a/spine/pending_requests.go +++ b/spine/pending_requests.go @@ -8,14 +8,6 @@ import ( "github.com/enbility/eebus-go/spine/model" ) -type PendingRequests interface { - Add(ski string, counter model.MsgCounterType, maxDelay time.Duration) - SetData(ski string, counter model.MsgCounterType, data any) *model.ErrorType - SetResult(ski string, counter model.MsgCounterType, errorResult *model.ErrorType) *model.ErrorType - GetData(ski string, counter model.MsgCounterType) (any, *model.ErrorType) - Remove(ski string, counter model.MsgCounterType) *model.ErrorType -} - type dataErrorPair struct { data any errorResult *model.ErrorType diff --git a/spine/send.go b/spine/send.go index 843e8b28..55f4010a 100644 --- a/spine/send.go +++ b/spine/send.go @@ -11,34 +11,6 @@ import ( lru "github.com/hashicorp/golang-lru/v2" ) -type ComControl interface { - // This must be connected to the correct remote device !! - SendSpineMessage(datagram model.DatagramType) error -} - -//go:generate mockery --name=Sender - -type Sender interface { - // Sends a read cmd to request some data - Request(cmdClassifier model.CmdClassifierType, senderAddress, destinationAddress *model.FeatureAddressType, ackRequest bool, cmd []model.CmdType) (*model.MsgCounterType, error) - // Sends a result cmd with no error to indicate that a message was processed successfully - ResultSuccess(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType) error - // Sends a result cmd with error information to indicate that a message processing failed - ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error - // Sends a reply cmd to response to a read cmd - Reply(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, cmd model.CmdType) error - // Sends a call cmd with a subscription request - Subscribe(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) - // Sends a call cmd with a binding request - Bind(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) - // Sends a notify cmd to indicate that a subscribed feature changed - Notify(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) - // Sends a write cmd, setting properties of remote features - Write(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) - // return the datagram for a given msgCounter (only availbe for Notify messasges!), error if not found - DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) -} - type SenderImpl struct { msgNum uint64 // 64bit values need to be defined on top of the struct to make atomic commands work on 32bit systems diff --git a/spine/subscription_manager.go b/spine/subscription_manager.go index 18ecf711..d05ae14b 100644 --- a/spine/subscription_manager.go +++ b/spine/subscription_manager.go @@ -12,15 +12,6 @@ import ( "github.com/enbility/eebus-go/util" ) -type SubscriptionManager interface { - AddSubscription(remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error - RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error - RemoveSubscriptionsForDevice(remoteDevice *DeviceRemoteImpl) - RemoveSubscriptionsForEntity(remoteEntity *EntityRemoteImpl) - Subscriptions(remoteDevice *DeviceRemoteImpl) []*SubscriptionEntry - SubscriptionsOnFeature(featureAddress model.FeatureAddressType) []*SubscriptionEntry -} - type SubscriptionEntry struct { id uint64 serverFeature FeatureLocal diff --git a/spine/types.go b/spine/types.go deleted file mode 100644 index 2d75e077..00000000 --- a/spine/types.go +++ /dev/null @@ -1,21 +0,0 @@ -package spine - -import "github.com/enbility/eebus-go/spine/model" - -//go:generate mockery --name=SpineDataProcessing - -// Used to pass an incoming SPINE message from a SHIP connection to the proper DeviceRemoteImpl -// -// Implemented by DeviceRemoteImpl, used by ShipConnection -type SpineDataProcessing interface { - HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) -} - -//go:generate mockery --name=SpineDataConnection - -// Used to pass an outgoing SPINE message from a DeviceLocalImpl to the SHIP connection -// -// Implemented by ShipConnection, used by DeviceLocalImpl -type SpineDataConnection interface { - WriteSpineMessage(message []byte) -} From b60dddc8d8fa4c8e9dee6c0ab9ef6a159faaa6f0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:19:20 +0100 Subject: [PATCH 120/240] Move ship interfaces into api file --- ship/api.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ ship/types.go | 62 -------------------------------------------------- 2 files changed, 63 insertions(+), 62 deletions(-) create mode 100644 ship/api.go diff --git a/ship/api.go b/ship/api.go new file mode 100644 index 00000000..48b15c4a --- /dev/null +++ b/ship/api.go @@ -0,0 +1,63 @@ +package ship + +//go:generate mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider +//go:generate mockery --name=ShipDataConnection +//go:generate mockery --name=ShipConnection + +type ShipConnection interface { + DataHandler() ShipDataConnection + CloseConnection(safe bool, code int, reason string) + RemoteSKI() string + ApprovePendingHandshake() + AbortPendingHandshake() + ShipHandshakeState() (ShipMessageExchangeState, error) +} + +// interface for handling the actual remote device data connection +// +// implemented by websocketConnection, used by ShipConnection +type ShipDataConnection interface { + // initialize data processing + InitDataProcessing(ShipDataProcessing) + + // send data via the connection to the remote device + WriteMessageToDataConnection([]byte) error + + // close the data connection + CloseDataConnection(closeCode int, reason string) + + // report if the data connection is closed and the error if availab le + IsDataConnectionClosed() (bool, error) +} + +// interface for handling incoming data +// +// implemented by shipConnection, used by websocketConnection +type ShipDataProcessing interface { + // called for each incoming message + HandleIncomingShipMessage([]byte) + + // called if the data connection is closed unsafe + // e.g. due to connection issues + ReportConnectionError(error) +} + +// interface for getting service wide information +// +// implemented by connectionsHub, used by shipConnection +type ShipServiceDataProvider interface { + // check if the SKI is paired + IsRemoteServiceForSKIPaired(string) bool + + // report closing of a connection and if handshake did complete + HandleConnectionClosed(ShipConnection, bool) + + // report the ship ID provided during the handshake + ReportServiceShipID(string, string) + + // check if the user is still able to trust the connection + AllowWaitingForTrust(string) bool + + // report the updated SHIP handshake state and optional error message for a SKI + HandleShipHandshakeStateUpdate(string, ShipState) +} diff --git a/ship/types.go b/ship/types.go index 075b04d3..a6ca4d34 100644 --- a/ship/types.go +++ b/ship/types.go @@ -109,65 +109,3 @@ const ( ) var shipInit []byte = []byte{model.MsgTypeInit, 0x00} - -//go:generate mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider -//go:generate mockery --name=ShipDataConnection -//go:generate mockery --name=ShipConnection - -type ShipConnection interface { - DataHandler() ShipDataConnection - CloseConnection(safe bool, code int, reason string) - RemoteSKI() string - ApprovePendingHandshake() - AbortPendingHandshake() - ShipHandshakeState() (ShipMessageExchangeState, error) -} - -// interface for handling the actual remote device data connection -// -// implemented by websocketConnection, used by ShipConnection -type ShipDataConnection interface { - // initialize data processing - InitDataProcessing(ShipDataProcessing) - - // send data via the connection to the remote device - WriteMessageToDataConnection([]byte) error - - // close the data connection - CloseDataConnection(closeCode int, reason string) - - // report if the data connection is closed and the error if availab le - IsDataConnectionClosed() (bool, error) -} - -// interface for handling incoming data -// -// implemented by shipConnection, used by websocketConnection -type ShipDataProcessing interface { - // called for each incoming message - HandleIncomingShipMessage([]byte) - - // called if the data connection is closed unsafe - // e.g. due to connection issues - ReportConnectionError(error) -} - -// interface for getting service wide information -// -// implemented by connectionsHub, used by shipConnection -type ShipServiceDataProvider interface { - // check if the SKI is paired - IsRemoteServiceForSKIPaired(string) bool - - // report closing of a connection and if handshake did complete - HandleConnectionClosed(ShipConnection, bool) - - // report the ship ID provided during the handshake - ReportServiceShipID(string, string) - - // check if the user is still able to trust the connection - AllowWaitingForTrust(string) bool - - // report the updated SHIP handshake state and optional error message for a SKI - HandleShipHandshakeStateUpdate(string, ShipState) -} From b1ad959b2c4d2e0b82a8a1d33ee388ad99e20f63 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:21:26 +0100 Subject: [PATCH 121/240] Move service interfaces into api file --- service/api.go | 100 ++++++++++++++++++++++++++++++++++++++++++ service/hub.go | 37 ---------------- service/mdns.go | 17 ------- service/mdns/types.go | 12 ----- service/service.go | 27 ------------ 5 files changed, 100 insertions(+), 93 deletions(-) create mode 100644 service/api.go diff --git a/service/api.go b/service/api.go new file mode 100644 index 00000000..9a5c9dfb --- /dev/null +++ b/service/api.go @@ -0,0 +1,100 @@ +package service + +import "net" + +/* EEBUSService */ + +//go:generate mockgen -destination=mock_service_test.go -package=service github.com/enbility/eebus-go/service EEBUSServiceHandler + +// interface for receiving data for specific events +type EEBUSServiceHandler interface { + // report all currently visible EEBUS services + VisibleRemoteServicesUpdated(service *EEBUSService, entries []RemoteService) + + // report a connection to a SKI + RemoteSKIConnected(service *EEBUSService, ski string) + + // report a disconnection to a SKI + RemoteSKIDisconnected(service *EEBUSService, ski string) + + // Provides the SHIP ID the remote service reported during the handshake process + // This needs to be persisted and passed on for future remote service connections + // when using `PairRemoteService` + ServiceShipIDUpdate(ski string, shipdID string) + + // Provides the current pairing state for the remote service + // This is called whenever the state changes and can be used to + // provide user information for the pairing/connection process + ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) + + // return if the user is still able to trust the connection + AllowWaitingForTrust(ski string) bool +} + +/* Hub */ + +//go:generate mockgen -destination=mock_hub_test.go -package=service github.com/enbility/eebus-go/service ServiceProvider,ConnectionsHub + +// interface for reporting data from connectionsHub to the EEBUSService +type ServiceProvider interface { + // report a newly discovered remote EEBUS service + VisibleMDNSRecordsUpdated(entries []*MdnsEntry) + + // report a connection to a SKI + RemoteSKIConnected(ski string) + + // report a disconnection to a SKI + RemoteSKIDisconnected(ski string) + + // provide the SHIP ID received during SHIP handshake process + // the ID needs to be stored and then provided for remote services so it can be compared and verified + ServiceShipIDUpdate(ski string, shipID string) + + // provides the current handshake state for a given SKI + ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) + + // return if the user is still able to trust the connection + AllowWaitingForTrust(ski string) bool +} + +type ConnectionsHub interface { + PairingDetailForSki(ski string) *ConnectionStateDetail + StartBrowseMdnsSearch() + StopBrowseMdnsSearch() + Start() + Shutdown() + ServiceForSKI(ski string) *ServiceDetails + RegisterRemoteSKI(ski string, enable bool) + InitiatePairingWithSKI(ski string) + CancelPairingWithSKI(ski string) + DisconnectSKI(ski string, reason string) +} + +/* Mdns */ + +//go:generate mockgen -destination=mock_mdns_test.go -package=service github.com/enbility/eebus-go/service MdnsSearch,MdnsService + +// implemented by hubConnection, used by mdns +type MdnsSearch interface { + ReportMdnsEntries(entries map[string]*MdnsEntry) +} + +// implemented by mdns, used by hubConnection +type MdnsService interface { + SetupMdnsService() error + ShutdownMdnsService() + AnnounceMdnsEntry() error + UnannounceMdnsEntry() + RegisterMdnsSearch(cb MdnsSearch) + UnregisterMdnsSearch(cb MdnsSearch) +} + +//go:generate mockery --name=MdnsProvider + +type MdnsProvider interface { + CheckAvailability() bool + Shutdown() + Announce(serviceName string, port int, txt []string) error + Unannounce() + ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) +} diff --git a/service/hub.go b/service/hub.go index 7928579c..d64ac931 100644 --- a/service/hub.go +++ b/service/hub.go @@ -40,43 +40,6 @@ var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ {min: 10, max: 20}, } -//go:generate mockgen -destination=mock_hub_test.go -package=service github.com/enbility/eebus-go/service ServiceProvider,ConnectionsHub - -// interface for reporting data from connectionsHub to the EEBUSService -type ServiceProvider interface { - // report a newly discovered remote EEBUS service - VisibleMDNSRecordsUpdated(entries []*MdnsEntry) - - // report a connection to a SKI - RemoteSKIConnected(ski string) - - // report a disconnection to a SKI - RemoteSKIDisconnected(ski string) - - // provide the SHIP ID received during SHIP handshake process - // the ID needs to be stored and then provided for remote services so it can be compared and verified - ServiceShipIDUpdate(ski string, shipID string) - - // provides the current handshake state for a given SKI - ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) - - // return if the user is still able to trust the connection - AllowWaitingForTrust(ski string) bool -} - -type ConnectionsHub interface { - PairingDetailForSki(ski string) *ConnectionStateDetail - StartBrowseMdnsSearch() - StopBrowseMdnsSearch() - Start() - Shutdown() - ServiceForSKI(ski string) *ServiceDetails - RegisterRemoteSKI(ski string, enable bool) - InitiatePairingWithSKI(ski string) - CancelPairingWithSKI(ski string) - DisconnectSKI(ski string, reason string) -} - // handling all connections to remote services type connectionsHubImpl struct { connections map[string]ship.ShipConnection diff --git a/service/mdns.go b/service/mdns.go index 369ffb84..21216187 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -29,23 +29,6 @@ type MdnsEntry struct { Addresses []net.IP // mandatory } -//go:generate mockgen -destination=mock_mdns_test.go -package=service github.com/enbility/eebus-go/service MdnsSearch,MdnsService - -// implemented by hubConnection, used by mdns -type MdnsSearch interface { - ReportMdnsEntries(entries map[string]*MdnsEntry) -} - -// implemented by mdns, used by hubConnection -type MdnsService interface { - SetupMdnsService() error - ShutdownMdnsService() - AnnounceMdnsEntry() error - UnannounceMdnsEntry() - RegisterMdnsSearch(cb MdnsSearch) - UnregisterMdnsSearch(cb MdnsSearch) -} - type mdnsManager struct { configuration *Configuration ski string diff --git a/service/mdns/types.go b/service/mdns/types.go index 97b520d2..b331744c 100644 --- a/service/mdns/types.go +++ b/service/mdns/types.go @@ -1,16 +1,4 @@ package mdns -import "net" - const shipZeroConfServiceType = "_ship._tcp" const shipZeroConfDomain = "local." - -//go:generate mockery --name=MdnsProvider - -type MdnsProvider interface { - CheckAvailability() bool - Shutdown() - Announce(serviceName string, port int, txt []string) error - Unannounce() - ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) -} diff --git a/service/service.go b/service/service.go index 6308a1d3..25ff602c 100644 --- a/service/service.go +++ b/service/service.go @@ -20,33 +20,6 @@ type RemoteService struct { Model string `json:"model"` } -//go:generate mockgen -destination=mock_service_test.go -package=service github.com/enbility/eebus-go/service EEBUSServiceHandler - -// interface for receiving data for specific events -type EEBUSServiceHandler interface { - // report all currently visible EEBUS services - VisibleRemoteServicesUpdated(service *EEBUSService, entries []RemoteService) - - // report a connection to a SKI - RemoteSKIConnected(service *EEBUSService, ski string) - - // report a disconnection to a SKI - RemoteSKIDisconnected(service *EEBUSService, ski string) - - // Provides the SHIP ID the remote service reported during the handshake process - // This needs to be persisted and passed on for future remote service connections - // when using `PairRemoteService` - ServiceShipIDUpdate(ski string, shipdID string) - - // Provides the current pairing state for the remote service - // This is called whenever the state changes and can be used to - // provide user information for the pairing/connection process - ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) - - // return if the user is still able to trust the connection - AllowWaitingForTrust(ski string) bool -} - // A service is the central element of an EEBUS service // including its websocket server and a zeroconf service. type EEBUSService struct { From 3a85095fd28d9bf042d58cf23bcf84a83bc5a75f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:23:14 +0100 Subject: [PATCH 122/240] Move mdns package interface into api file --- service/api.go | 12 ------------ service/mdns/api.go | 13 +++++++++++++ 2 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 service/mdns/api.go diff --git a/service/api.go b/service/api.go index 9a5c9dfb..98892b79 100644 --- a/service/api.go +++ b/service/api.go @@ -1,7 +1,5 @@ package service -import "net" - /* EEBUSService */ //go:generate mockgen -destination=mock_service_test.go -package=service github.com/enbility/eebus-go/service EEBUSServiceHandler @@ -88,13 +86,3 @@ type MdnsService interface { RegisterMdnsSearch(cb MdnsSearch) UnregisterMdnsSearch(cb MdnsSearch) } - -//go:generate mockery --name=MdnsProvider - -type MdnsProvider interface { - CheckAvailability() bool - Shutdown() - Announce(serviceName string, port int, txt []string) error - Unannounce() - ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) -} diff --git a/service/mdns/api.go b/service/mdns/api.go new file mode 100644 index 00000000..f7c383fb --- /dev/null +++ b/service/mdns/api.go @@ -0,0 +1,13 @@ +package mdns + +import "net" + +//go:generate mockery --name=MdnsProvider + +type MdnsProvider interface { + CheckAvailability() bool + Shutdown() + Announce(serviceName string, port int, txt []string) error + Unannounce() + ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) +} From 16762829fd0a2479f7626ba4eb1fea8380b61bc3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:34:17 +0100 Subject: [PATCH 123/240] Add ErrorType tests --- spine/model/custom_error.go | 2 +- spine/model/custom_error_test.go | 62 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 spine/model/custom_error_test.go diff --git a/spine/model/custom_error.go b/spine/model/custom_error.go index e2122626..e70ef971 100644 --- a/spine/model/custom_error.go +++ b/spine/model/custom_error.go @@ -29,7 +29,7 @@ func NewErrorTypeFromString(description string) *ErrorType { } func NewErrorTypeFromResult(result *ResultDataType) *ErrorType { - if *result.ErrorNumber == ErrorNumberTypeNoError { + if result.ErrorNumber == nil || *result.ErrorNumber == ErrorNumberTypeNoError { return nil } diff --git a/spine/model/custom_error_test.go b/spine/model/custom_error_test.go new file mode 100644 index 00000000..6a591fd0 --- /dev/null +++ b/spine/model/custom_error_test.go @@ -0,0 +1,62 @@ +package model + +import ( + "testing" + + "github.com/enbility/eebus-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestErrorTypeSuite(t *testing.T) { + suite.Run(t, new(ErrorTypeSuite)) +} + +type ErrorTypeSuite struct { + suite.Suite +} + +func (s *ErrorTypeSuite) SetupSuite() {} +func (s *ErrorTypeSuite) TearDownTest() {} + +func (s *ErrorTypeSuite) BeforeTest(suiteName, testName string) {} + +func (s *ErrorTypeSuite) Test_NewErrorType() { + result := NewErrorType(ErrorNumberTypeNoError, "") + assert.NotNil(s.T(), result) +} + +func (s *ErrorTypeSuite) Test_NewErrorTypeFromNumber() { + result := NewErrorTypeFromNumber(ErrorNumberTypeCommandRejected) + assert.NotNil(s.T(), result) +} + +func (s *ErrorTypeSuite) Test_NewErrorTypeFromString() { + result := NewErrorTypeFromString("error") + assert.NotNil(s.T(), result) + + assert.NotEqual(s.T(), 0, len(result.String())) +} + +func (s *ErrorTypeSuite) Test_NewErrorTypeFromResult() { + input := &ResultDataType{} + + result := NewErrorTypeFromResult(input) + assert.Nil(s.T(), result) + + input = &ResultDataType{ + ErrorNumber: util.Ptr(ErrorNumberTypeNoError), + } + + result = NewErrorTypeFromResult(input) + assert.Nil(s.T(), result) + + input = &ResultDataType{ + ErrorNumber: util.Ptr(ErrorNumberTypeCommandNotSupported), + } + + result = NewErrorTypeFromResult(input) + assert.NotNil(s.T(), result) + + assert.NotEqual(s.T(), 0, len(result.String())) +} From 191db177805e92e88e9c6cd193c741437d7ccf8f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:44:10 +0100 Subject: [PATCH 124/240] Move model tests to model package --- spine/model/alarm_additions_test.go | 25 +- spine/model/bill_additions_test.go | 67 ++-- .../model/bindingmanagement_additions_test.go | 25 +- spine/model/collection_operations_test.go | 7 +- spine/model/commandframe_additions_test.go | 93 +++--- spine/model/datagram_additions_test.go | 105 ++++--- .../deviceconfiguration_additions_test.go | 73 +++-- .../electricalconnection_additions_test.go | 293 +++++++++--------- spine/model/hvac_additions_test.go | 167 +++++----- spine/model/identification_additions_test.go | 29 +- spine/model/loadcontrol_additions_test.go | 119 ++++--- spine/model/measurement_additions_test.go | 137 ++++---- spine/model/messaging_additions_test.go | 25 +- .../operatingconstraints_additions_test.go | 133 ++++---- spine/model/powersequences_additions_test.go | 219 +++++++------ spine/model/setpoint_additions_test.go | 47 ++- .../subscriptionmanagement_additions_test.go | 25 +- .../model/supplyconditions_additions_test.go | 69 ++--- .../model/tariffinformation_additions_test.go | 267 ++++++++-------- spine/model/taskmanagement_additions_test.go | 79 +++-- spine/model/threshold_additions_test.go | 69 ++--- spine/model/timeseries_additions_test.go | 175 ++++++----- spine/model/timetable_additions_test.go | 69 ++--- spine/model/update_test.go | 31 +- 24 files changed, 1162 insertions(+), 1186 deletions(-) diff --git a/spine/model/alarm_additions_test.go b/spine/model/alarm_additions_test.go index 6f947eb4..10f26ec0 100644 --- a/spine/model/alarm_additions_test.go +++ b/spine/model/alarm_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestAlarmListDataType_Update(t *testing.T) { - sut := model.AlarmListDataType{ - AlarmListData: []model.AlarmDataType{ + sut := AlarmListDataType{ + AlarmListData: []AlarmDataType{ { - AlarmId: util.Ptr(model.AlarmIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + AlarmId: util.Ptr(AlarmIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - AlarmId: util.Ptr(model.AlarmIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + AlarmId: util.Ptr(AlarmIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.AlarmListDataType{ - AlarmListData: []model.AlarmDataType{ + newData := AlarmListDataType{ + AlarmListData: []AlarmDataType{ { - AlarmId: util.Ptr(model.AlarmIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + AlarmId: util.Ptr(AlarmIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.AlarmListData // check the non changing items diff --git a/spine/model/bill_additions_test.go b/spine/model/bill_additions_test.go index 50fa8db7..9d29809e 100644 --- a/spine/model/bill_additions_test.go +++ b/spine/model/bill_additions_test.go @@ -1,76 +1,75 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestBillListDataType_Update(t *testing.T) { - sut := model.BillListDataType{ - BillData: []model.BillDataType{ + sut := BillListDataType{ + BillData: []BillDataType{ { - BillId: util.Ptr(model.BillIdType(0)), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + BillId: util.Ptr(BillIdType(0)), + ScopeType: util.Ptr(ScopeTypeTypeACCurrent), }, { - BillId: util.Ptr(model.BillIdType(1)), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + BillId: util.Ptr(BillIdType(1)), + ScopeType: util.Ptr(ScopeTypeTypeACCurrent), }, }, } - newData := model.BillListDataType{ - BillData: []model.BillDataType{ + newData := BillListDataType{ + BillData: []BillDataType{ { - BillId: util.Ptr(model.BillIdType(1)), - ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + BillId: util.Ptr(BillIdType(1)), + ScopeType: util.Ptr(ScopeTypeTypeACPower), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.BillData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.BillId)) - assert.Equal(t, model.ScopeTypeTypeACCurrent, *item1.ScopeType) + assert.Equal(t, ScopeTypeTypeACCurrent, *item1.ScopeType) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.BillId)) - assert.Equal(t, model.ScopeTypeTypeACPower, *item2.ScopeType) + assert.Equal(t, ScopeTypeTypeACPower, *item2.ScopeType) } func TestBillConstraintsListDataType_Update(t *testing.T) { - sut := model.BillConstraintsListDataType{ - BillConstraintsData: []model.BillConstraintsDataType{ + sut := BillConstraintsListDataType{ + BillConstraintsData: []BillConstraintsDataType{ { - BillId: util.Ptr(model.BillIdType(0)), - PositionCountMin: util.Ptr(model.BillPositionCountType(0)), + BillId: util.Ptr(BillIdType(0)), + PositionCountMin: util.Ptr(BillPositionCountType(0)), }, { - BillId: util.Ptr(model.BillIdType(1)), - PositionCountMin: util.Ptr(model.BillPositionCountType(0)), + BillId: util.Ptr(BillIdType(1)), + PositionCountMin: util.Ptr(BillPositionCountType(0)), }, }, } - newData := model.BillConstraintsListDataType{ - BillConstraintsData: []model.BillConstraintsDataType{ + newData := BillConstraintsListDataType{ + BillConstraintsData: []BillConstraintsDataType{ { - BillId: util.Ptr(model.BillIdType(1)), - PositionCountMin: util.Ptr(model.BillPositionCountType(1)), + BillId: util.Ptr(BillIdType(1)), + PositionCountMin: util.Ptr(BillPositionCountType(1)), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.BillConstraintsData // check the non changing items @@ -85,30 +84,30 @@ func TestBillConstraintsListDataType_Update(t *testing.T) { } func TestBillDescriptionListDataType_Update(t *testing.T) { - sut := model.BillDescriptionListDataType{ - BillDescriptionData: []model.BillDescriptionDataType{ + sut := BillDescriptionListDataType{ + BillDescriptionData: []BillDescriptionDataType{ { - BillId: util.Ptr(model.BillIdType(0)), + BillId: util.Ptr(BillIdType(0)), UpdateRequired: util.Ptr(false), }, { - BillId: util.Ptr(model.BillIdType(1)), + BillId: util.Ptr(BillIdType(1)), UpdateRequired: util.Ptr(false), }, }, } - newData := model.BillDescriptionListDataType{ - BillDescriptionData: []model.BillDescriptionDataType{ + newData := BillDescriptionListDataType{ + BillDescriptionData: []BillDescriptionDataType{ { - BillId: util.Ptr(model.BillIdType(1)), + BillId: util.Ptr(BillIdType(1)), UpdateRequired: util.Ptr(true), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.BillDescriptionData // check the non changing items diff --git a/spine/model/bindingmanagement_additions_test.go b/spine/model/bindingmanagement_additions_test.go index f1a14f2f..009db2b4 100644 --- a/spine/model/bindingmanagement_additions_test.go +++ b/spine/model/bindingmanagement_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestBindingManagementEntryListDataType_Update(t *testing.T) { - sut := model.BindingManagementEntryListDataType{ - BindingManagementEntryData: []model.BindingManagementEntryDataType{ + sut := BindingManagementEntryListDataType{ + BindingManagementEntryData: []BindingManagementEntryDataType{ { - BindingId: util.Ptr(model.BindingIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + BindingId: util.Ptr(BindingIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - BindingId: util.Ptr(model.BindingIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + BindingId: util.Ptr(BindingIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.BindingManagementEntryListDataType{ - BindingManagementEntryData: []model.BindingManagementEntryDataType{ + newData := BindingManagementEntryListDataType{ + BindingManagementEntryData: []BindingManagementEntryDataType{ { - BindingId: util.Ptr(model.BindingIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + BindingId: util.Ptr(BindingIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.BindingManagementEntryData // check the non changing items diff --git a/spine/model/collection_operations_test.go b/spine/model/collection_operations_test.go index 660ffd26..c22f67ec 100644 --- a/spine/model/collection_operations_test.go +++ b/spine/model/collection_operations_test.go @@ -1,10 +1,9 @@ -package model_test +package model import ( "fmt" "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) @@ -28,7 +27,7 @@ func TestUnion_NewData(t *testing.T) { } // Act - result := model.Merge(existingData, newData) + result := Merge(existingData, newData) if assert.Equal(t, 2, len(result)) { assert.Equal(t, 1, int(*result[0].id)) @@ -50,7 +49,7 @@ func TestUnion_NewAndUpdateData(t *testing.T) { } // Act - result := model.Merge(existingData, newData) + result := Merge(existingData, newData) if assert.Equal(t, 3, len(result)) { assert.Equal(t, 1, int(*result[0].id)) diff --git a/spine/model/commandframe_additions_test.go b/spine/model/commandframe_additions_test.go index ca423335..0a1e97d6 100644 --- a/spine/model/commandframe_additions_test.go +++ b/spine/model/commandframe_additions_test.go @@ -1,20 +1,19 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestFilterType_Selector_Data(t *testing.T) { - data := &model.ElectricalConnectionDescriptionListDataSelectorsType{ - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), - ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + data := &ElectricalConnectionDescriptionListDataSelectorsType{ + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), + ScopeType: util.Ptr(ScopeTypeTypeACPower), } - sut := &model.FilterType{ + sut := &FilterType{ ElectricalConnectionDescriptionListDataSelectors: data, } @@ -22,31 +21,31 @@ func TestFilterType_Selector_Data(t *testing.T) { cmdData, err := sut.Data() assert.Nil(t, err) assert.NotNil(t, cmdData) - assert.Equal(t, model.FunctionTypeElectricalConnectionDescriptionListData, *cmdData.Function) + assert.Equal(t, FunctionTypeElectricalConnectionDescriptionListData, *cmdData.Function) assert.Equal(t, data, cmdData.Selector) } func TestFilterType_Selector_SetDataForFunction(t *testing.T) { - cmd := model.FilterType{} - cmd.SetDataForFunction(model.EEBusTagTypeTypeSelector, model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionListDataSelectorsType{}) + cmd := FilterType{} + cmd.SetDataForFunction(EEBusTagTypeTypeSelector, FunctionTypeElectricalConnectionDescriptionListData, &ElectricalConnectionDescriptionListDataSelectorsType{}) assert.NotNil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) - cmd = model.FilterType{} - cmd.SetDataForFunction(model.EEBusTagTypeTypeSelector, model.FunctionTypeElectricalConnectionDescriptionListData, nil) + cmd = FilterType{} + cmd.SetDataForFunction(EEBusTagTypeTypeSelector, FunctionTypeElectricalConnectionDescriptionListData, nil) assert.Nil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) - var test *model.ElectricalConnectionDescriptionListDataSelectorsType - cmd = model.FilterType{} - cmd.SetDataForFunction(model.EEBusTagTypeTypeSelector, model.FunctionTypeElectricalConnectionDescriptionListData, test) + var test *ElectricalConnectionDescriptionListDataSelectorsType + cmd = FilterType{} + cmd.SetDataForFunction(EEBusTagTypeTypeSelector, FunctionTypeElectricalConnectionDescriptionListData, test) assert.NotNil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) } func TestFilterType_Elements_Data(t *testing.T) { - data := &model.ElectricalConnectionDescriptionDataElementsType{ - ElectricalConnectionId: util.Ptr(model.ElementTagType{}), + data := &ElectricalConnectionDescriptionDataElementsType{ + ElectricalConnectionId: util.Ptr(ElementTagType{}), } - sut := &model.FilterType{ + sut := &FilterType{ ElectricalConnectionDescriptionDataElements: data, } @@ -54,31 +53,31 @@ func TestFilterType_Elements_Data(t *testing.T) { cmdData, err := sut.Data() assert.Nil(t, err) assert.NotNil(t, cmdData) - assert.Equal(t, model.FunctionTypeElectricalConnectionDescriptionListData, *cmdData.Function) + assert.Equal(t, FunctionTypeElectricalConnectionDescriptionListData, *cmdData.Function) assert.Equal(t, data, cmdData.Elements) } func TestFilterType_Elements_SetDataForFunction(t *testing.T) { - cmd := model.FilterType{} - cmd.SetDataForFunction(model.EEbusTagTypeTypeElements, model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionDataElementsType{}) + cmd := FilterType{} + cmd.SetDataForFunction(EEbusTagTypeTypeElements, FunctionTypeElectricalConnectionDescriptionListData, &ElectricalConnectionDescriptionDataElementsType{}) assert.NotNil(t, cmd.ElectricalConnectionDescriptionDataElements) - cmd = model.FilterType{} - cmd.SetDataForFunction(model.EEbusTagTypeTypeElements, model.FunctionTypeElectricalConnectionDescriptionListData, nil) + cmd = FilterType{} + cmd.SetDataForFunction(EEbusTagTypeTypeElements, FunctionTypeElectricalConnectionDescriptionListData, nil) assert.Nil(t, cmd.ElectricalConnectionDescriptionDataElements) - var test *model.ElectricalConnectionDescriptionDataElementsType - cmd = model.FilterType{} - cmd.SetDataForFunction(model.EEbusTagTypeTypeElements, model.FunctionTypeElectricalConnectionDescriptionListData, test) + var test *ElectricalConnectionDescriptionDataElementsType + cmd = FilterType{} + cmd.SetDataForFunction(EEbusTagTypeTypeElements, FunctionTypeElectricalConnectionDescriptionListData, test) assert.NotNil(t, cmd.ElectricalConnectionDescriptionDataElements) } func TestCmdType_Data(t *testing.T) { - data := &model.NodeManagementDetailedDiscoveryDataType{ - SpecificationVersionList: &model.NodeManagementSpecificationVersionListType{[]model.SpecificationVersionDataType{model.SpecificationVersionDataType("dummy")}}, + data := &NodeManagementDetailedDiscoveryDataType{ + SpecificationVersionList: &NodeManagementSpecificationVersionListType{[]SpecificationVersionDataType{SpecificationVersionDataType("dummy")}}, } - sut := &model.CmdType{ + sut := &CmdType{ NodeManagementDetailedDiscoveryData: data, } @@ -87,28 +86,28 @@ func TestCmdType_Data(t *testing.T) { assert.Nil(t, err) assert.NotNil(t, cmdData) assert.Equal(t, "NodeManagementDetailedDiscoveryData", cmdData.FieldName) - assert.Equal(t, model.FunctionTypeNodeManagementDetailedDiscoveryData, *cmdData.Function) + assert.Equal(t, FunctionTypeNodeManagementDetailedDiscoveryData, *cmdData.Function) assert.Equal(t, data, cmdData.Value) } func TestCmdType_SetDataForFunction(t *testing.T) { - cmd := model.CmdType{} - cmd.SetDataForFunction(model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionListDataType{}) + cmd := CmdType{} + cmd.SetDataForFunction(FunctionTypeElectricalConnectionDescriptionListData, &ElectricalConnectionDescriptionListDataType{}) assert.NotNil(t, cmd.ElectricalConnectionDescriptionListData) - cmd = model.CmdType{} - cmd.SetDataForFunction(model.FunctionTypeElectricalConnectionDescriptionListData, nil) + cmd = CmdType{} + cmd.SetDataForFunction(FunctionTypeElectricalConnectionDescriptionListData, nil) assert.Nil(t, cmd.ElectricalConnectionDescriptionListData) - var test *model.ElectricalConnectionDescriptionListDataType - cmd = model.CmdType{} - cmd.SetDataForFunction(model.FunctionTypeElectricalConnectionDescriptionListData, test) + var test *ElectricalConnectionDescriptionListDataType + cmd = CmdType{} + cmd.SetDataForFunction(FunctionTypeElectricalConnectionDescriptionListData, test) assert.NotNil(t, cmd.ElectricalConnectionDescriptionListData) } func TestCmdType_ExtractFilter_NoFilter(t *testing.T) { - sut := &model.CmdType{ - NodeManagementDetailedDiscoveryData: &model.NodeManagementDetailedDiscoveryDataType{}, + sut := &CmdType{ + NodeManagementDetailedDiscoveryData: &NodeManagementDetailedDiscoveryDataType{}, } // Act @@ -119,18 +118,18 @@ func TestCmdType_ExtractFilter_NoFilter(t *testing.T) { func TestCmdType_ExtractFilter_FilterPartialDelete(t *testing.T) { - filterP := model.FilterType{ - CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}, - NodeManagementDetailedDiscoveryDataSelectors: &model.NodeManagementDetailedDiscoveryDataSelectorsType{}, + filterP := FilterType{ + CmdControl: &CmdControlType{Partial: &ElementTagType{}}, + NodeManagementDetailedDiscoveryDataSelectors: &NodeManagementDetailedDiscoveryDataSelectorsType{}, } - filterD := model.FilterType{ - CmdControl: &model.CmdControlType{Delete: &model.ElementTagType{}}, - NodeManagementDetailedDiscoveryDataSelectors: &model.NodeManagementDetailedDiscoveryDataSelectorsType{}, + filterD := FilterType{ + CmdControl: &CmdControlType{Delete: &ElementTagType{}}, + NodeManagementDetailedDiscoveryDataSelectors: &NodeManagementDetailedDiscoveryDataSelectorsType{}, } - sut := &model.CmdType{ - Filter: []model.FilterType{filterD, filterP}, - NodeManagementDetailedDiscoveryData: &model.NodeManagementDetailedDiscoveryDataType{}, + sut := &CmdType{ + Filter: []FilterType{filterD, filterP}, + NodeManagementDetailedDiscoveryData: &NodeManagementDetailedDiscoveryDataType{}, } // Act diff --git a/spine/model/datagram_additions_test.go b/spine/model/datagram_additions_test.go index 249ad45b..0a423ef9 100644 --- a/spine/model/datagram_additions_test.go +++ b/spine/model/datagram_additions_test.go @@ -1,26 +1,25 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" ) func TestPrintMessageOverview_Read_Send(t *testing.T) { - datagram := &model.DatagramType{ - Header: model.HeaderType{ - AddressSource: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), + datagram := &DatagramType{ + Header: HeaderType{ + AddressSource: &FeatureAddressType{ + Device: util.Ptr(AddressDeviceType("localdevice")), }, - AddressDestination: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), + AddressDestination: &FeatureAddressType{ + Device: util.Ptr(AddressDeviceType("localdevice")), }, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeRead), + MsgCounter: util.Ptr(MsgCounterType(1)), + CmdClassifier: util.Ptr(CmdClassifierTypeRead), }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{ + Payload: PayloadType{ + Cmd: []CmdType{ {}, }, }, @@ -30,17 +29,17 @@ func TestPrintMessageOverview_Read_Send(t *testing.T) { } func TestPrintMessageOverview_Read_Recv(t *testing.T) { - datagram := &model.DatagramType{ - Header: model.HeaderType{ - AddressSource: &model.FeatureAddressType{}, - AddressDestination: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), + datagram := &DatagramType{ + Header: HeaderType{ + AddressSource: &FeatureAddressType{}, + AddressDestination: &FeatureAddressType{ + Device: util.Ptr(AddressDeviceType("localdevice")), }, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeRead), + MsgCounter: util.Ptr(MsgCounterType(1)), + CmdClassifier: util.Ptr(CmdClassifierTypeRead), }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{ + Payload: PayloadType{ + Cmd: []CmdType{ {}, }, }, @@ -50,18 +49,18 @@ func TestPrintMessageOverview_Read_Recv(t *testing.T) { } func TestPrintMessageOverview_Reply_Recv(t *testing.T) { - datagram := &model.DatagramType{ - Header: model.HeaderType{ - AddressSource: &model.FeatureAddressType{}, - AddressDestination: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), + datagram := &DatagramType{ + Header: HeaderType{ + AddressSource: &FeatureAddressType{}, + AddressDestination: &FeatureAddressType{ + Device: util.Ptr(AddressDeviceType("localdevice")), }, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - MsgCounterReference: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeReply), + MsgCounter: util.Ptr(MsgCounterType(1)), + MsgCounterReference: util.Ptr(MsgCounterType(1)), + CmdClassifier: util.Ptr(CmdClassifierTypeReply), }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{ + Payload: PayloadType{ + Cmd: []CmdType{ {}, }, }, @@ -71,21 +70,21 @@ func TestPrintMessageOverview_Reply_Recv(t *testing.T) { } func TestPrintMessageOverview_Result_Recv(t *testing.T) { - datagram := &model.DatagramType{ - Header: model.HeaderType{ - AddressSource: &model.FeatureAddressType{}, - AddressDestination: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), + datagram := &DatagramType{ + Header: HeaderType{ + AddressSource: &FeatureAddressType{}, + AddressDestination: &FeatureAddressType{ + Device: util.Ptr(AddressDeviceType("localdevice")), }, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - MsgCounterReference: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeResult), + MsgCounter: util.Ptr(MsgCounterType(1)), + MsgCounterReference: util.Ptr(MsgCounterType(1)), + CmdClassifier: util.Ptr(CmdClassifierTypeResult), }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{ + Payload: PayloadType{ + Cmd: []CmdType{ { - ResultData: &model.ResultDataType{ - ErrorNumber: util.Ptr(model.ErrorNumberType(1)), + ResultData: &ResultDataType{ + ErrorNumber: util.Ptr(ErrorNumberType(1)), }, }, }, @@ -96,18 +95,18 @@ func TestPrintMessageOverview_Result_Recv(t *testing.T) { } func TestPrintMessageOverview_Write_Recv(t *testing.T) { - datagram := &model.DatagramType{ - Header: model.HeaderType{ - AddressSource: &model.FeatureAddressType{}, - AddressDestination: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), + datagram := &DatagramType{ + Header: HeaderType{ + AddressSource: &FeatureAddressType{}, + AddressDestination: &FeatureAddressType{ + Device: util.Ptr(AddressDeviceType("localdevice")), }, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - MsgCounterReference: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeWrite), + MsgCounter: util.Ptr(MsgCounterType(1)), + MsgCounterReference: util.Ptr(MsgCounterType(1)), + CmdClassifier: util.Ptr(CmdClassifierTypeWrite), }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{ + Payload: PayloadType{ + Cmd: []CmdType{ {}, }, }, diff --git a/spine/model/deviceconfiguration_additions_test.go b/spine/model/deviceconfiguration_additions_test.go index 020165d4..d509b809 100644 --- a/spine/model/deviceconfiguration_additions_test.go +++ b/spine/model/deviceconfiguration_additions_test.go @@ -1,36 +1,35 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestDeviceConfigurationKeyValueListDataType_Update(t *testing.T) { - sut := model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + sut := DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []DeviceConfigurationKeyValueDataType{ { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - Value: &model.DeviceConfigurationKeyValueValueType{ + KeyId: util.Ptr(DeviceConfigurationKeyIdType(0)), + Value: &DeviceConfigurationKeyValueValueType{ Boolean: util.Ptr(true), }, }, { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(1)), - Value: &model.DeviceConfigurationKeyValueValueType{ + KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), + Value: &DeviceConfigurationKeyValueValueType{ Boolean: util.Ptr(true), }, }, }, } - newData := model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + newData := DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []DeviceConfigurationKeyValueDataType{ { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(1)), - Value: &model.DeviceConfigurationKeyValueValueType{ + KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), + Value: &DeviceConfigurationKeyValueValueType{ Boolean: util.Ptr(false), }, }, @@ -38,7 +37,7 @@ func TestDeviceConfigurationKeyValueListDataType_Update(t *testing.T) { } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.DeviceConfigurationKeyValueData // check the non changing items @@ -53,66 +52,66 @@ func TestDeviceConfigurationKeyValueListDataType_Update(t *testing.T) { } func TestDeviceConfigurationKeyValueDescriptionListDataType_Update(t *testing.T) { - sut := model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + sut := DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []DeviceConfigurationKeyValueDescriptionDataType{ { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeBoolean), + KeyId: util.Ptr(DeviceConfigurationKeyIdType(0)), + ValueType: util.Ptr(DeviceConfigurationKeyValueTypeTypeBoolean), }, { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(1)), - ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeBoolean), + KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), + ValueType: util.Ptr(DeviceConfigurationKeyValueTypeTypeBoolean), }, }, } - newData := model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + newData := DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []DeviceConfigurationKeyValueDescriptionDataType{ { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(1)), - ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeString), + KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), + ValueType: util.Ptr(DeviceConfigurationKeyValueTypeTypeString), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.DeviceConfigurationKeyValueDescriptionData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.KeyId)) - assert.Equal(t, model.DeviceConfigurationKeyValueTypeTypeBoolean, *item1.ValueType) + assert.Equal(t, DeviceConfigurationKeyValueTypeTypeBoolean, *item1.ValueType) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.KeyId)) - assert.Equal(t, model.DeviceConfigurationKeyValueTypeTypeString, *item2.ValueType) + assert.Equal(t, DeviceConfigurationKeyValueTypeTypeString, *item2.ValueType) } func TestDeviceConfigurationKeyValueConstraintsListDataType_Update(t *testing.T) { - sut := model.DeviceConfigurationKeyValueConstraintsListDataType{ - DeviceConfigurationKeyValueConstraintsData: []model.DeviceConfigurationKeyValueConstraintsDataType{ + sut := DeviceConfigurationKeyValueConstraintsListDataType{ + DeviceConfigurationKeyValueConstraintsData: []DeviceConfigurationKeyValueConstraintsDataType{ { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - ValueStepSize: &model.DeviceConfigurationKeyValueValueType{ + KeyId: util.Ptr(DeviceConfigurationKeyIdType(0)), + ValueStepSize: &DeviceConfigurationKeyValueValueType{ Boolean: util.Ptr(true), }, }, { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(1)), - ValueStepSize: &model.DeviceConfigurationKeyValueValueType{ + KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), + ValueStepSize: &DeviceConfigurationKeyValueValueType{ Boolean: util.Ptr(true), }, }, }, } - newData := model.DeviceConfigurationKeyValueConstraintsListDataType{ - DeviceConfigurationKeyValueConstraintsData: []model.DeviceConfigurationKeyValueConstraintsDataType{ + newData := DeviceConfigurationKeyValueConstraintsListDataType{ + DeviceConfigurationKeyValueConstraintsData: []DeviceConfigurationKeyValueConstraintsDataType{ { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(1)), - ValueStepSize: &model.DeviceConfigurationKeyValueValueType{ + KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), + ValueStepSize: &DeviceConfigurationKeyValueValueType{ Boolean: util.Ptr(false), }, }, @@ -120,7 +119,7 @@ func TestDeviceConfigurationKeyValueConstraintsListDataType_Update(t *testing.T) } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.DeviceConfigurationKeyValueConstraintsData // check the non changing items diff --git a/spine/model/electricalconnection_additions_test.go b/spine/model/electricalconnection_additions_test.go index bb533973..38e95d74 100644 --- a/spine/model/electricalconnection_additions_test.go +++ b/spine/model/electricalconnection_additions_test.go @@ -1,106 +1,105 @@ -package model_test +package model import ( "encoding/json" "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestElectricalConnectionStateListDataType_Update(t *testing.T) { - sut := model.ElectricalConnectionStateListDataType{ - ElectricalConnectionStateData: []model.ElectricalConnectionStateDataType{ + sut := ElectricalConnectionStateListDataType{ + ElectricalConnectionStateData: []ElectricalConnectionStateDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - CurrentEnergyMode: util.Ptr(model.EnergyModeTypeProduce), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + CurrentEnergyMode: util.Ptr(EnergyModeTypeProduce), }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), - CurrentEnergyMode: util.Ptr(model.EnergyModeTypeProduce), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), + CurrentEnergyMode: util.Ptr(EnergyModeTypeProduce), }, }, } - newData := model.ElectricalConnectionStateListDataType{ - ElectricalConnectionStateData: []model.ElectricalConnectionStateDataType{ + newData := ElectricalConnectionStateListDataType{ + ElectricalConnectionStateData: []ElectricalConnectionStateDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), - CurrentEnergyMode: util.Ptr(model.EnergyModeTypeConsume), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), + CurrentEnergyMode: util.Ptr(EnergyModeTypeConsume), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ElectricalConnectionStateData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, model.EnergyModeTypeProduce, *item1.CurrentEnergyMode) + assert.Equal(t, EnergyModeTypeProduce, *item1.CurrentEnergyMode) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.ElectricalConnectionId)) - assert.Equal(t, model.EnergyModeTypeConsume, *item2.CurrentEnergyMode) + assert.Equal(t, EnergyModeTypeConsume, *item2.CurrentEnergyMode) } // verifies that a subset of existing items will be updated with identified new values func TestElectricalConnectionPermittedValueSetListDataType_Update_Modify(t *testing.T) { - sut := model.ElectricalConnectionPermittedValueSetListDataType{ - ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + sut := ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []ElectricalConnectionPermittedValueSetDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - PermittedValueSet: []model.ScaledNumberSetType{ + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(0)), + PermittedValueSet: []ScaledNumberSetType{ { - Range: []model.ScaledNumberRangeType{ + Range: []ScaledNumberRangeType{ { - Min: model.NewScaledNumberType(1), + Min: NewScaledNumberType(1), }, }, }, }, }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)), - PermittedValueSet: []model.ScaledNumberSetType{ + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(1)), + PermittedValueSet: []ScaledNumberSetType{ { - Range: []model.ScaledNumberRangeType{ + Range: []ScaledNumberRangeType{ { - Min: model.NewScaledNumberType(6), - Max: model.NewScaledNumberType(16), + Min: NewScaledNumberType(6), + Max: NewScaledNumberType(16), }, }, }, }, }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)), - PermittedValueSet: []model.ScaledNumberSetType{ + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(2)), + PermittedValueSet: []ScaledNumberSetType{ { - Range: []model.ScaledNumberRangeType{ + Range: []ScaledNumberRangeType{ { - Min: model.NewScaledNumberType(6), - Max: model.NewScaledNumberType(16), + Min: NewScaledNumberType(6), + Max: NewScaledNumberType(16), }, }, }, }, }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(3)), - PermittedValueSet: []model.ScaledNumberSetType{ + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(3)), + PermittedValueSet: []ScaledNumberSetType{ { - Range: []model.ScaledNumberRangeType{ + Range: []ScaledNumberRangeType{ { - Min: model.NewScaledNumberType(6), - Max: model.NewScaledNumberType(16), + Min: NewScaledNumberType(6), + Max: NewScaledNumberType(16), }, }, }, @@ -109,45 +108,45 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Modify(t *test }, } - newData := model.ElectricalConnectionPermittedValueSetListDataType{ - ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + newData := ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []ElectricalConnectionPermittedValueSetDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)), - PermittedValueSet: []model.ScaledNumberSetType{ + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(1)), + PermittedValueSet: []ScaledNumberSetType{ { - Range: []model.ScaledNumberRangeType{ + Range: []ScaledNumberRangeType{ { - Min: model.NewScaledNumberType(2), - Max: model.NewScaledNumberType(16), + Min: NewScaledNumberType(2), + Max: NewScaledNumberType(16), }, }, }, }, }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)), - PermittedValueSet: []model.ScaledNumberSetType{ + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(2)), + PermittedValueSet: []ScaledNumberSetType{ { - Range: []model.ScaledNumberRangeType{ + Range: []ScaledNumberRangeType{ { - Min: model.NewScaledNumberType(2), - Max: model.NewScaledNumberType(16), + Min: NewScaledNumberType(2), + Max: NewScaledNumberType(16), }, }, }, }, }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(3)), - PermittedValueSet: []model.ScaledNumberSetType{ + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(3)), + PermittedValueSet: []ScaledNumberSetType{ { - Range: []model.ScaledNumberRangeType{ + Range: []ScaledNumberRangeType{ { - Min: model.NewScaledNumberType(2), - Max: model.NewScaledNumberType(16), + Min: NewScaledNumberType(2), + Max: NewScaledNumberType(16), }, }, }, @@ -157,7 +156,7 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Modify(t *test } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ElectricalConnectionPermittedValueSetData // check the non changing items @@ -240,7 +239,7 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Modify_Selecto ] }` - var sut model.ElectricalConnectionPermittedValueSetListDataType + var sut ElectricalConnectionPermittedValueSetListDataType err := json.Unmarshal([]byte(existingDataJson), &sut) if assert.Nil(t, err) == false { return @@ -263,19 +262,19 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Modify_Selecto ] }` - var newData model.ElectricalConnectionPermittedValueSetListDataType + var newData ElectricalConnectionPermittedValueSetListDataType err = json.Unmarshal([]byte(newDataJson), &newData) if assert.Nil(t, err) == false { return } - partial := &model.FilterType{ - CmdControl: &model.CmdControlType{ - Partial: &model.ElementTagType{}, + partial := &FilterType{ + CmdControl: &CmdControlType{ + Partial: &ElementTagType{}, }, - ElectricalConnectionPermittedValueSetListDataSelectors: &model.ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[model.ElectricalConnectionIdType](0), - ParameterId: util.Ptr[model.ElectricalConnectionParameterIdType](1), + ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ + ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), + ParameterId: util.Ptr[ElectricalConnectionParameterIdType](1), }, } @@ -373,7 +372,7 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Modify( ] }` - var sut model.ElectricalConnectionPermittedValueSetListDataType + var sut ElectricalConnectionPermittedValueSetListDataType err := json.Unmarshal([]byte(existingDataJson), &sut) if assert.Nil(t, err) == false { return @@ -426,24 +425,24 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Modify( ] }` - var newData model.ElectricalConnectionPermittedValueSetListDataType + var newData ElectricalConnectionPermittedValueSetListDataType err = json.Unmarshal([]byte(newDataJson), &newData) if assert.Nil(t, err) == false { return } - delete := &model.FilterType{ - CmdControl: &model.CmdControlType{ - Delete: &model.ElementTagType{}, + delete := &FilterType{ + CmdControl: &CmdControlType{ + Delete: &ElementTagType{}, }, - ElectricalConnectionPermittedValueSetListDataSelectors: &model.ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[model.ElectricalConnectionIdType](0), - ParameterId: util.Ptr[model.ElectricalConnectionParameterIdType](0), + ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ + ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), + ParameterId: util.Ptr[ElectricalConnectionParameterIdType](0), }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), delete) + sut.UpdateList(&newData, NewFilterTypePartial(), delete) data := sut.ElectricalConnectionPermittedValueSetData // check the deleted item is gone @@ -522,19 +521,19 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete(t *test ] }` - var sut model.ElectricalConnectionPermittedValueSetListDataType + var sut ElectricalConnectionPermittedValueSetListDataType err := json.Unmarshal([]byte(existingDataJson), &sut) if assert.Nil(t, err) == false { return } - delete := &model.FilterType{ - CmdControl: &model.CmdControlType{ - Delete: &model.ElementTagType{}, + delete := &FilterType{ + CmdControl: &CmdControlType{ + Delete: &ElementTagType{}, }, - ElectricalConnectionPermittedValueSetListDataSelectors: &model.ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[model.ElectricalConnectionIdType](0), - ParameterId: util.Ptr[model.ElectricalConnectionParameterIdType](0), + ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ + ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), + ParameterId: util.Ptr[ElectricalConnectionParameterIdType](0), }, } @@ -618,22 +617,22 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Element ] }` - var sut model.ElectricalConnectionPermittedValueSetListDataType + var sut ElectricalConnectionPermittedValueSetListDataType err := json.Unmarshal([]byte(existingDataJson), &sut) if assert.Nil(t, err) == false { return } - delete := &model.FilterType{ - CmdControl: &model.CmdControlType{ - Delete: &model.ElementTagType{}, + delete := &FilterType{ + CmdControl: &CmdControlType{ + Delete: &ElementTagType{}, }, - ElectricalConnectionPermittedValueSetDataElements: &model.ElectricalConnectionPermittedValueSetDataElementsType{ - PermittedValueSet: &model.ElementTagType{}, + ElectricalConnectionPermittedValueSetDataElements: &ElectricalConnectionPermittedValueSetDataElementsType{ + PermittedValueSet: &ElementTagType{}, }, - ElectricalConnectionPermittedValueSetListDataSelectors: &model.ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[model.ElectricalConnectionIdType](0), - ParameterId: util.Ptr[model.ElectricalConnectionParameterIdType](0), + ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ + ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), + ParameterId: util.Ptr[ElectricalConnectionParameterIdType](0), }, } @@ -647,7 +646,7 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Element item1 := data[0] assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) assert.Equal(t, 0, int(*item1.ParameterId)) - var nilValue []model.ScaledNumberSetType + var nilValue []ScaledNumberSetType assert.Equal(t, nilValue, item1.PermittedValueSet) // check properties of remaining item @@ -724,18 +723,18 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_OnlyEle ] }` - var sut model.ElectricalConnectionPermittedValueSetListDataType + var sut ElectricalConnectionPermittedValueSetListDataType err := json.Unmarshal([]byte(existingDataJson), &sut) if assert.Nil(t, err) == false { return } - delete := &model.FilterType{ - CmdControl: &model.CmdControlType{ - Delete: &model.ElementTagType{}, + delete := &FilterType{ + CmdControl: &CmdControlType{ + Delete: &ElementTagType{}, }, - ElectricalConnectionPermittedValueSetDataElements: &model.ElectricalConnectionPermittedValueSetDataElementsType{ - PermittedValueSet: &model.ElementTagType{}, + ElectricalConnectionPermittedValueSetDataElements: &ElectricalConnectionPermittedValueSetDataElementsType{ + PermittedValueSet: &ElementTagType{}, }, } @@ -749,7 +748,7 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_OnlyEle item1 := data[0] assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) assert.Equal(t, 0, int(*item1.ParameterId)) - var nilValue []model.ScaledNumberSetType + var nilValue []ScaledNumberSetType assert.Equal(t, nilValue, item1.PermittedValueSet) // check properties @@ -831,7 +830,7 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Add(t * ] }` - var sut model.ElectricalConnectionPermittedValueSetListDataType + var sut ElectricalConnectionPermittedValueSetListDataType err := json.Unmarshal([]byte(existingDataJson), &sut) if assert.Nil(t, err) == false { return @@ -897,24 +896,24 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Add(t * ] }` - var newData model.ElectricalConnectionPermittedValueSetListDataType + var newData ElectricalConnectionPermittedValueSetListDataType err = json.Unmarshal([]byte(newDataJson), &newData) if assert.Nil(t, err) == false { return } - delete := &model.FilterType{ - CmdControl: &model.CmdControlType{ - Delete: &model.ElementTagType{}, + delete := &FilterType{ + CmdControl: &CmdControlType{ + Delete: &ElementTagType{}, }, - ElectricalConnectionPermittedValueSetListDataSelectors: &model.ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[model.ElectricalConnectionIdType](0), - ParameterId: util.Ptr[model.ElectricalConnectionParameterIdType](0), + ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ + ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), + ParameterId: util.Ptr[ElectricalConnectionParameterIdType](0), }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), delete) + sut.UpdateList(&newData, NewFilterTypePartial(), delete) data := sut.ElectricalConnectionPermittedValueSetData // check the deleted item is added again @@ -956,7 +955,7 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_NewItem(t *tes ] }` - var sut model.ElectricalConnectionPermittedValueSetListDataType + var sut ElectricalConnectionPermittedValueSetListDataType err := json.Unmarshal([]byte(existingDataJson), &sut) if assert.Nil(t, err) == false { return @@ -989,14 +988,14 @@ func TestElectricalConnectionPermittedValueSetListDataType_Update_NewItem(t *tes ] }` - var newData model.ElectricalConnectionPermittedValueSetListDataType + var newData ElectricalConnectionPermittedValueSetListDataType err = json.Unmarshal([]byte(newDataJson), &newData) if assert.Nil(t, err) == false { return } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ElectricalConnectionPermittedValueSetData // new item should be added @@ -1047,7 +1046,7 @@ func TestElectricalConnectionPermittedValueSetListDataType_UpdateWithoutIdenifie } ] }` - var sut model.ElectricalConnectionPermittedValueSetListDataType + var sut ElectricalConnectionPermittedValueSetListDataType err := json.Unmarshal([]byte(existingDataJson), &sut) if assert.Nil(t, err) == false { return @@ -1071,14 +1070,14 @@ func TestElectricalConnectionPermittedValueSetListDataType_UpdateWithoutIdenifie ] }` - var newData model.ElectricalConnectionPermittedValueSetListDataType + var newData ElectricalConnectionPermittedValueSetListDataType err = json.Unmarshal([]byte(newDataJson), &newData) if assert.Nil(t, err) == false { return } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ElectricalConnectionPermittedValueSetData // the new item should not be added @@ -1109,81 +1108,81 @@ func TestElectricalConnectionPermittedValueSetListDataType_UpdateWithoutIdenifie } func TestElectricalConnectionDescriptionListDataType_Update(t *testing.T) { - sut := model.ElectricalConnectionDescriptionListDataType{ - ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + sut := ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []ElectricalConnectionDescriptionDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + PowerSupplyType: util.Ptr(ElectricalConnectionVoltageTypeTypeAc), }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), - PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), + PowerSupplyType: util.Ptr(ElectricalConnectionVoltageTypeTypeAc), }, }, } - newData := model.ElectricalConnectionDescriptionListDataType{ - ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + newData := ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []ElectricalConnectionDescriptionDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), - PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeDc), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), + PowerSupplyType: util.Ptr(ElectricalConnectionVoltageTypeTypeDc), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ElectricalConnectionDescriptionData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, model.ElectricalConnectionVoltageTypeTypeAc, *item1.PowerSupplyType) + assert.Equal(t, ElectricalConnectionVoltageTypeTypeAc, *item1.PowerSupplyType) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.ElectricalConnectionId)) - assert.Equal(t, model.ElectricalConnectionVoltageTypeTypeDc, *item2.PowerSupplyType) + assert.Equal(t, ElectricalConnectionVoltageTypeTypeDc, *item2.PowerSupplyType) } func TestElectricalConnectionParameterDescriptionListDataType_Update(t *testing.T) { - sut := model.ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + sut := ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []ElectricalConnectionParameterDescriptionDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(0)), + VoltageType: util.Ptr(ElectricalConnectionVoltageTypeTypeAc), }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(0)), + MeasurementId: util.Ptr(MeasurementIdType(0)), + VoltageType: util.Ptr(ElectricalConnectionVoltageTypeTypeAc), }, }, } - newData := model.ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + newData := ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []ElectricalConnectionParameterDescriptionDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeDc), + ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), + ParameterId: util.Ptr(ElectricalConnectionParameterIdType(0)), + VoltageType: util.Ptr(ElectricalConnectionVoltageTypeTypeDc), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ElectricalConnectionParameterDescriptionData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, model.ElectricalConnectionVoltageTypeTypeAc, *item1.VoltageType) + assert.Equal(t, ElectricalConnectionVoltageTypeTypeAc, *item1.VoltageType) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.ElectricalConnectionId)) - assert.Equal(t, model.ElectricalConnectionVoltageTypeTypeDc, *item2.VoltageType) + assert.Equal(t, ElectricalConnectionVoltageTypeTypeDc, *item2.VoltageType) } diff --git a/spine/model/hvac_additions_test.go b/spine/model/hvac_additions_test.go index 0574c774..13622b38 100644 --- a/spine/model/hvac_additions_test.go +++ b/spine/model/hvac_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestHvacSystemFunctionListDataType_Update(t *testing.T) { - sut := model.HvacSystemFunctionListDataType{ - HvacSystemFunctionData: []model.HvacSystemFunctionDataType{ + sut := HvacSystemFunctionListDataType{ + HvacSystemFunctionData: []HvacSystemFunctionDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(0)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), IsOverrunActive: util.Ptr(false), }, { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), IsOverrunActive: util.Ptr(false), }, }, } - newData := model.HvacSystemFunctionListDataType{ - HvacSystemFunctionData: []model.HvacSystemFunctionDataType{ + newData := HvacSystemFunctionListDataType{ + HvacSystemFunctionData: []HvacSystemFunctionDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), IsOverrunActive: util.Ptr(true), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.HvacSystemFunctionData // check the non changing items @@ -47,30 +46,30 @@ func TestHvacSystemFunctionListDataType_Update(t *testing.T) { } func TestHvacSystemFunctionOperationModeRelationListDataType_Update(t *testing.T) { - sut := model.HvacSystemFunctionOperationModeRelationListDataType{ - HvacSystemFunctionOperationModeRelationData: []model.HvacSystemFunctionOperationModeRelationDataType{ + sut := HvacSystemFunctionOperationModeRelationListDataType{ + HvacSystemFunctionOperationModeRelationData: []HvacSystemFunctionOperationModeRelationDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(0)), - OperationModeId: util.Ptr(model.HvacOperationModeIdType(0)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), + OperationModeId: util.Ptr(HvacOperationModeIdType(0)), }, { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), - OperationModeId: util.Ptr(model.HvacOperationModeIdType(0)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), + OperationModeId: util.Ptr(HvacOperationModeIdType(0)), }, }, } - newData := model.HvacSystemFunctionOperationModeRelationListDataType{ - HvacSystemFunctionOperationModeRelationData: []model.HvacSystemFunctionOperationModeRelationDataType{ + newData := HvacSystemFunctionOperationModeRelationListDataType{ + HvacSystemFunctionOperationModeRelationData: []HvacSystemFunctionOperationModeRelationDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), - OperationModeId: util.Ptr(model.HvacOperationModeIdType(1)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), + OperationModeId: util.Ptr(HvacOperationModeIdType(1)), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.HvacSystemFunctionOperationModeRelationData // check the non changing items @@ -85,30 +84,30 @@ func TestHvacSystemFunctionOperationModeRelationListDataType_Update(t *testing.T } func TestHvacSystemFunctionSetpointRelationListDataType_Update(t *testing.T) { - sut := model.HvacSystemFunctionSetpointRelationListDataType{ - HvacSystemFunctionSetpointRelationData: []model.HvacSystemFunctionSetpointRelationDataType{ + sut := HvacSystemFunctionSetpointRelationListDataType{ + HvacSystemFunctionSetpointRelationData: []HvacSystemFunctionSetpointRelationDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(0)), - OperationModeId: util.Ptr(model.HvacOperationModeIdType(0)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), + OperationModeId: util.Ptr(HvacOperationModeIdType(0)), }, { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), - OperationModeId: util.Ptr(model.HvacOperationModeIdType(0)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), + OperationModeId: util.Ptr(HvacOperationModeIdType(0)), }, }, } - newData := model.HvacSystemFunctionSetpointRelationListDataType{ - HvacSystemFunctionSetpointRelationData: []model.HvacSystemFunctionSetpointRelationDataType{ + newData := HvacSystemFunctionSetpointRelationListDataType{ + HvacSystemFunctionSetpointRelationData: []HvacSystemFunctionSetpointRelationDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), - OperationModeId: util.Ptr(model.HvacOperationModeIdType(1)), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), + OperationModeId: util.Ptr(HvacOperationModeIdType(1)), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.HvacSystemFunctionSetpointRelationData // check the non changing items @@ -123,30 +122,30 @@ func TestHvacSystemFunctionSetpointRelationListDataType_Update(t *testing.T) { } func TestHvacSystemFunctionPowerSequenceRelationListDataType_Update(t *testing.T) { - sut := model.HvacSystemFunctionPowerSequenceRelationListDataType{ - HvacSystemFunctionPowerSequenceRelationData: []model.HvacSystemFunctionPowerSequenceRelationDataType{ + sut := HvacSystemFunctionPowerSequenceRelationListDataType{ + HvacSystemFunctionPowerSequenceRelationData: []HvacSystemFunctionPowerSequenceRelationDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(0)), - SequenceId: []model.PowerSequenceIdType{0}, + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), + SequenceId: []PowerSequenceIdType{0}, }, { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), - SequenceId: []model.PowerSequenceIdType{0}, + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), + SequenceId: []PowerSequenceIdType{0}, }, }, } - newData := model.HvacSystemFunctionPowerSequenceRelationListDataType{ - HvacSystemFunctionPowerSequenceRelationData: []model.HvacSystemFunctionPowerSequenceRelationDataType{ + newData := HvacSystemFunctionPowerSequenceRelationListDataType{ + HvacSystemFunctionPowerSequenceRelationData: []HvacSystemFunctionPowerSequenceRelationDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), - SequenceId: []model.PowerSequenceIdType{1}, + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), + SequenceId: []PowerSequenceIdType{1}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.HvacSystemFunctionPowerSequenceRelationData // check the non changing items @@ -161,30 +160,30 @@ func TestHvacSystemFunctionPowerSequenceRelationListDataType_Update(t *testing.T } func TestHvacSystemFunctionDescriptionListDataType_Update(t *testing.T) { - sut := model.HvacSystemFunctionDescriptionListDataType{ - HvacSystemFunctionDescriptionData: []model.HvacSystemFunctionDescriptionDataType{ + sut := HvacSystemFunctionDescriptionListDataType{ + HvacSystemFunctionDescriptionData: []HvacSystemFunctionDescriptionDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.HvacSystemFunctionDescriptionListDataType{ - HvacSystemFunctionDescriptionData: []model.HvacSystemFunctionDescriptionDataType{ + newData := HvacSystemFunctionDescriptionListDataType{ + HvacSystemFunctionDescriptionData: []HvacSystemFunctionDescriptionDataType{ { - SystemFunctionId: util.Ptr(model.HvacSystemFunctionIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.HvacSystemFunctionDescriptionData // check the non changing items @@ -199,30 +198,30 @@ func TestHvacSystemFunctionDescriptionListDataType_Update(t *testing.T) { } func TestHvacOperationModeDescriptionListDataType_Update(t *testing.T) { - sut := model.HvacOperationModeDescriptionListDataType{ - HvacOperationModeDescriptionData: []model.HvacOperationModeDescriptionDataType{ + sut := HvacOperationModeDescriptionListDataType{ + HvacOperationModeDescriptionData: []HvacOperationModeDescriptionDataType{ { - OperationModeId: util.Ptr(model.HvacOperationModeIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + OperationModeId: util.Ptr(HvacOperationModeIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - OperationModeId: util.Ptr(model.HvacOperationModeIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + OperationModeId: util.Ptr(HvacOperationModeIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.HvacOperationModeDescriptionListDataType{ - HvacOperationModeDescriptionData: []model.HvacOperationModeDescriptionDataType{ + newData := HvacOperationModeDescriptionListDataType{ + HvacOperationModeDescriptionData: []HvacOperationModeDescriptionDataType{ { - OperationModeId: util.Ptr(model.HvacOperationModeIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + OperationModeId: util.Ptr(HvacOperationModeIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.HvacOperationModeDescriptionData // check the non changing items @@ -237,30 +236,30 @@ func TestHvacOperationModeDescriptionListDataType_Update(t *testing.T) { } func TestHvacOverrunListDataType_Update(t *testing.T) { - sut := model.HvacOverrunListDataType{ - HvacOverrunData: []model.HvacOverrunDataType{ + sut := HvacOverrunListDataType{ + HvacOverrunData: []HvacOverrunDataType{ { - OverrunId: util.Ptr(model.HvacOverrunIdType(0)), + OverrunId: util.Ptr(HvacOverrunIdType(0)), IsOverrunStatusChangeable: util.Ptr(false), }, { - OverrunId: util.Ptr(model.HvacOverrunIdType(1)), + OverrunId: util.Ptr(HvacOverrunIdType(1)), IsOverrunStatusChangeable: util.Ptr(false), }, }, } - newData := model.HvacOverrunListDataType{ - HvacOverrunData: []model.HvacOverrunDataType{ + newData := HvacOverrunListDataType{ + HvacOverrunData: []HvacOverrunDataType{ { - OverrunId: util.Ptr(model.HvacOverrunIdType(1)), + OverrunId: util.Ptr(HvacOverrunIdType(1)), IsOverrunStatusChangeable: util.Ptr(true), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.HvacOverrunData // check the non changing items @@ -275,30 +274,30 @@ func TestHvacOverrunListDataType_Update(t *testing.T) { } func TestHvacOverrunDescriptionListDataType_Update(t *testing.T) { - sut := model.HvacOverrunDescriptionListDataType{ - HvacOverrunDescriptionData: []model.HvacOverrunDescriptionDataType{ + sut := HvacOverrunDescriptionListDataType{ + HvacOverrunDescriptionData: []HvacOverrunDescriptionDataType{ { - OverrunId: util.Ptr(model.HvacOverrunIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + OverrunId: util.Ptr(HvacOverrunIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - OverrunId: util.Ptr(model.HvacOverrunIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + OverrunId: util.Ptr(HvacOverrunIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.HvacOverrunDescriptionListDataType{ - HvacOverrunDescriptionData: []model.HvacOverrunDescriptionDataType{ + newData := HvacOverrunDescriptionListDataType{ + HvacOverrunDescriptionData: []HvacOverrunDescriptionDataType{ { - OverrunId: util.Ptr(model.HvacOverrunIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + OverrunId: util.Ptr(HvacOverrunIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.HvacOverrunDescriptionData // check the non changing items diff --git a/spine/model/identification_additions_test.go b/spine/model/identification_additions_test.go index fd3b41f3..ba843cf5 100644 --- a/spine/model/identification_additions_test.go +++ b/spine/model/identification_additions_test.go @@ -1,47 +1,46 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestIdentificationListDataType_Update(t *testing.T) { - sut := model.IdentificationListDataType{ - IdentificationData: []model.IdentificationDataType{ + sut := IdentificationListDataType{ + IdentificationData: []IdentificationDataType{ { - IdentificationId: util.Ptr(model.IdentificationIdType(0)), - IdentificationType: util.Ptr(model.IdentificationTypeTypeEui48), + IdentificationId: util.Ptr(IdentificationIdType(0)), + IdentificationType: util.Ptr(IdentificationTypeTypeEui48), }, { - IdentificationId: util.Ptr(model.IdentificationIdType(1)), - IdentificationType: util.Ptr(model.IdentificationTypeTypeEui48), + IdentificationId: util.Ptr(IdentificationIdType(1)), + IdentificationType: util.Ptr(IdentificationTypeTypeEui48), }, }, } - newData := model.IdentificationListDataType{ - IdentificationData: []model.IdentificationDataType{ + newData := IdentificationListDataType{ + IdentificationData: []IdentificationDataType{ { - IdentificationId: util.Ptr(model.IdentificationIdType(1)), - IdentificationType: util.Ptr(model.IdentificationTypeTypeEui64), + IdentificationId: util.Ptr(IdentificationIdType(1)), + IdentificationType: util.Ptr(IdentificationTypeTypeEui64), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.IdentificationData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.IdentificationId)) - assert.Equal(t, model.IdentificationTypeTypeEui48, *item1.IdentificationType) + assert.Equal(t, IdentificationTypeTypeEui48, *item1.IdentificationType) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.IdentificationId)) - assert.Equal(t, model.IdentificationTypeTypeEui64, *item2.IdentificationType) + assert.Equal(t, IdentificationTypeTypeEui64, *item2.IdentificationType) } diff --git a/spine/model/loadcontrol_additions_test.go b/spine/model/loadcontrol_additions_test.go index c204ed51..23f380d8 100644 --- a/spine/model/loadcontrol_additions_test.go +++ b/spine/model/loadcontrol_additions_test.go @@ -1,114 +1,113 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestLoadControlEventListDataType_Update(t *testing.T) { - sut := model.LoadControlEventListDataType{ - LoadControlEventData: []model.LoadControlEventDataType{ + sut := LoadControlEventListDataType{ + LoadControlEventData: []LoadControlEventDataType{ { - EventId: util.Ptr(model.LoadControlEventIdType(0)), - EventActionConsume: util.Ptr(model.LoadControlEventActionTypeNormal), + EventId: util.Ptr(LoadControlEventIdType(0)), + EventActionConsume: util.Ptr(LoadControlEventActionTypeNormal), }, { - EventId: util.Ptr(model.LoadControlEventIdType(1)), - EventActionConsume: util.Ptr(model.LoadControlEventActionTypeNormal), + EventId: util.Ptr(LoadControlEventIdType(1)), + EventActionConsume: util.Ptr(LoadControlEventActionTypeNormal), }, }, } - newData := model.LoadControlEventListDataType{ - LoadControlEventData: []model.LoadControlEventDataType{ + newData := LoadControlEventListDataType{ + LoadControlEventData: []LoadControlEventDataType{ { - EventId: util.Ptr(model.LoadControlEventIdType(1)), - EventActionConsume: util.Ptr(model.LoadControlEventActionTypeIncrease), + EventId: util.Ptr(LoadControlEventIdType(1)), + EventActionConsume: util.Ptr(LoadControlEventActionTypeIncrease), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.LoadControlEventData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.EventId)) - assert.Equal(t, model.LoadControlEventActionTypeNormal, *item1.EventActionConsume) + assert.Equal(t, LoadControlEventActionTypeNormal, *item1.EventActionConsume) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.EventId)) - assert.Equal(t, model.LoadControlEventActionTypeIncrease, *item2.EventActionConsume) + assert.Equal(t, LoadControlEventActionTypeIncrease, *item2.EventActionConsume) } func TestLoadControlStateListDataType_Update(t *testing.T) { - sut := model.LoadControlStateListDataType{ - LoadControlStateData: []model.LoadControlStateDataType{ + sut := LoadControlStateListDataType{ + LoadControlStateData: []LoadControlStateDataType{ { - EventId: util.Ptr(model.LoadControlEventIdType(0)), - EventStateConsume: util.Ptr(model.LoadControlEventStateTypeEventAccepted), + EventId: util.Ptr(LoadControlEventIdType(0)), + EventStateConsume: util.Ptr(LoadControlEventStateTypeEventAccepted), }, { - EventId: util.Ptr(model.LoadControlEventIdType(1)), - EventStateConsume: util.Ptr(model.LoadControlEventStateTypeEventAccepted), + EventId: util.Ptr(LoadControlEventIdType(1)), + EventStateConsume: util.Ptr(LoadControlEventStateTypeEventAccepted), }, }, } - newData := model.LoadControlStateListDataType{ - LoadControlStateData: []model.LoadControlStateDataType{ + newData := LoadControlStateListDataType{ + LoadControlStateData: []LoadControlStateDataType{ { - EventId: util.Ptr(model.LoadControlEventIdType(1)), - EventStateConsume: util.Ptr(model.LoadControlEventStateTypeEventStopped), + EventId: util.Ptr(LoadControlEventIdType(1)), + EventStateConsume: util.Ptr(LoadControlEventStateTypeEventStopped), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.LoadControlStateData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.EventId)) - assert.Equal(t, model.LoadControlEventStateTypeEventAccepted, *item1.EventStateConsume) + assert.Equal(t, LoadControlEventStateTypeEventAccepted, *item1.EventStateConsume) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.EventId)) - assert.Equal(t, model.LoadControlEventStateTypeEventStopped, *item2.EventStateConsume) + assert.Equal(t, LoadControlEventStateTypeEventStopped, *item2.EventStateConsume) } func TestLoadControlLimitListDataType_Update(t *testing.T) { - sut := model.LoadControlLimitListDataType{ - LoadControlLimitData: []model.LoadControlLimitDataType{ + sut := LoadControlLimitListDataType{ + LoadControlLimitData: []LoadControlLimitDataType{ { - LimitId: util.Ptr(model.LoadControlLimitIdType(0)), + LimitId: util.Ptr(LoadControlLimitIdType(0)), IsLimitChangeable: util.Ptr(false), }, { - LimitId: util.Ptr(model.LoadControlLimitIdType(1)), + LimitId: util.Ptr(LoadControlLimitIdType(1)), IsLimitChangeable: util.Ptr(false), }, }, } - newData := model.LoadControlLimitListDataType{ - LoadControlLimitData: []model.LoadControlLimitDataType{ + newData := LoadControlLimitListDataType{ + LoadControlLimitData: []LoadControlLimitDataType{ { - LimitId: util.Ptr(model.LoadControlLimitIdType(1)), + LimitId: util.Ptr(LoadControlLimitIdType(1)), IsLimitChangeable: util.Ptr(true), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.LoadControlLimitData // check the non changing items @@ -123,30 +122,30 @@ func TestLoadControlLimitListDataType_Update(t *testing.T) { } func TestLoadControlLimitConstraintsListDataType_Update(t *testing.T) { - sut := model.LoadControlLimitConstraintsListDataType{ - LoadControlLimitConstraintsData: []model.LoadControlLimitConstraintsDataType{ + sut := LoadControlLimitConstraintsListDataType{ + LoadControlLimitConstraintsData: []LoadControlLimitConstraintsDataType{ { - LimitId: util.Ptr(model.LoadControlLimitIdType(0)), - ValueStepSize: model.NewScaledNumberType(1), + LimitId: util.Ptr(LoadControlLimitIdType(0)), + ValueStepSize: NewScaledNumberType(1), }, { - LimitId: util.Ptr(model.LoadControlLimitIdType(1)), - ValueStepSize: model.NewScaledNumberType(1), + LimitId: util.Ptr(LoadControlLimitIdType(1)), + ValueStepSize: NewScaledNumberType(1), }, }, } - newData := model.LoadControlLimitConstraintsListDataType{ - LoadControlLimitConstraintsData: []model.LoadControlLimitConstraintsDataType{ + newData := LoadControlLimitConstraintsListDataType{ + LoadControlLimitConstraintsData: []LoadControlLimitConstraintsDataType{ { - LimitId: util.Ptr(model.LoadControlLimitIdType(1)), - ValueStepSize: model.NewScaledNumberType(10), + LimitId: util.Ptr(LoadControlLimitIdType(1)), + ValueStepSize: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.LoadControlLimitConstraintsData // check the non changing items @@ -161,39 +160,39 @@ func TestLoadControlLimitConstraintsListDataType_Update(t *testing.T) { } func TestLoadControlLimitDescriptionListDataType_Update(t *testing.T) { - sut := model.LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + sut := LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []LoadControlLimitDescriptionDataType{ { - LimitId: util.Ptr(model.LoadControlLimitIdType(0)), - LimitCategory: util.Ptr(model.LoadControlCategoryTypeObligation), + LimitId: util.Ptr(LoadControlLimitIdType(0)), + LimitCategory: util.Ptr(LoadControlCategoryTypeObligation), }, { - LimitId: util.Ptr(model.LoadControlLimitIdType(1)), - LimitCategory: util.Ptr(model.LoadControlCategoryTypeObligation), + LimitId: util.Ptr(LoadControlLimitIdType(1)), + LimitCategory: util.Ptr(LoadControlCategoryTypeObligation), }, }, } - newData := model.LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + newData := LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []LoadControlLimitDescriptionDataType{ { - LimitId: util.Ptr(model.LoadControlLimitIdType(1)), - LimitCategory: util.Ptr(model.LoadControlCategoryTypeOptimization), + LimitId: util.Ptr(LoadControlLimitIdType(1)), + LimitCategory: util.Ptr(LoadControlCategoryTypeOptimization), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.LoadControlLimitDescriptionData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.LimitId)) - assert.Equal(t, model.LoadControlCategoryTypeObligation, *item1.LimitCategory) + assert.Equal(t, LoadControlCategoryTypeObligation, *item1.LimitCategory) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.LimitId)) - assert.Equal(t, model.LoadControlCategoryTypeOptimization, *item2.LimitCategory) + assert.Equal(t, LoadControlCategoryTypeOptimization, *item2.LimitCategory) } diff --git a/spine/model/measurement_additions_test.go b/spine/model/measurement_additions_test.go index cec94b09..81a63831 100644 --- a/spine/model/measurement_additions_test.go +++ b/spine/model/measurement_additions_test.go @@ -1,123 +1,122 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestMeasurementListDataType_Update_Add(t *testing.T) { - sut := model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ + sut := MeasurementListDataType{ + MeasurementData: []MeasurementDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ValueType: util.Ptr(model.MeasurementValueTypeTypeAverageValue), - Value: model.NewScaledNumberType(1), + MeasurementId: util.Ptr(MeasurementIdType(0)), + ValueType: util.Ptr(MeasurementValueTypeTypeAverageValue), + Value: NewScaledNumberType(1), }, { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ValueType: util.Ptr(model.MeasurementValueTypeTypeAverageValue), - Value: model.NewScaledNumberType(1), + MeasurementId: util.Ptr(MeasurementIdType(1)), + ValueType: util.Ptr(MeasurementValueTypeTypeAverageValue), + Value: NewScaledNumberType(1), }, }, } - newData := model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ + newData := MeasurementListDataType{ + MeasurementData: []MeasurementDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), - Value: model.NewScaledNumberType(10), + MeasurementId: util.Ptr(MeasurementIdType(1)), + ValueType: util.Ptr(MeasurementValueTypeTypeValue), + Value: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.MeasurementData // check the non changing items assert.Equal(t, 3, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.MeasurementId)) - assert.Equal(t, model.MeasurementValueTypeTypeAverageValue, *item1.ValueType) + assert.Equal(t, MeasurementValueTypeTypeAverageValue, *item1.ValueType) assert.Equal(t, 1.0, item1.Value.GetValue()) item2 := data[1] assert.Equal(t, 1, int(*item2.MeasurementId)) - assert.Equal(t, model.MeasurementValueTypeTypeAverageValue, *item2.ValueType) + assert.Equal(t, MeasurementValueTypeTypeAverageValue, *item2.ValueType) assert.Equal(t, 1.0, item2.Value.GetValue()) } func TestMeasurementListDataType_Update_Replace(t *testing.T) { - sut := model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ + sut := MeasurementListDataType{ + MeasurementData: []MeasurementDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ValueType: util.Ptr(model.MeasurementValueTypeTypeAverageValue), - Value: model.NewScaledNumberType(1), + MeasurementId: util.Ptr(MeasurementIdType(0)), + ValueType: util.Ptr(MeasurementValueTypeTypeAverageValue), + Value: NewScaledNumberType(1), }, { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), - Value: model.NewScaledNumberType(1), + MeasurementId: util.Ptr(MeasurementIdType(1)), + ValueType: util.Ptr(MeasurementValueTypeTypeValue), + Value: NewScaledNumberType(1), }, }, } - newData := model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ + newData := MeasurementListDataType{ + MeasurementData: []MeasurementDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), - Value: model.NewScaledNumberType(10), + MeasurementId: util.Ptr(MeasurementIdType(1)), + ValueType: util.Ptr(MeasurementValueTypeTypeValue), + Value: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.MeasurementData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.MeasurementId)) - assert.Equal(t, model.MeasurementValueTypeTypeAverageValue, *item1.ValueType) + assert.Equal(t, MeasurementValueTypeTypeAverageValue, *item1.ValueType) assert.Equal(t, 1.0, item1.Value.GetValue()) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.MeasurementId)) - assert.Equal(t, model.MeasurementValueTypeTypeValue, *item2.ValueType) + assert.Equal(t, MeasurementValueTypeTypeValue, *item2.ValueType) assert.Equal(t, 10.0, item2.Value.GetValue()) } func TestMeasurementConstraintsListDataType_Update(t *testing.T) { - sut := model.MeasurementConstraintsListDataType{ - MeasurementConstraintsData: []model.MeasurementConstraintsDataType{ + sut := MeasurementConstraintsListDataType{ + MeasurementConstraintsData: []MeasurementConstraintsDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ValueStepSize: model.NewScaledNumberType(1), + MeasurementId: util.Ptr(MeasurementIdType(0)), + ValueStepSize: NewScaledNumberType(1), }, { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ValueStepSize: model.NewScaledNumberType(1), + MeasurementId: util.Ptr(MeasurementIdType(1)), + ValueStepSize: NewScaledNumberType(1), }, }, } - newData := model.MeasurementConstraintsListDataType{ - MeasurementConstraintsData: []model.MeasurementConstraintsDataType{ + newData := MeasurementConstraintsListDataType{ + MeasurementConstraintsData: []MeasurementConstraintsDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ValueStepSize: model.NewScaledNumberType(10), + MeasurementId: util.Ptr(MeasurementIdType(1)), + ValueStepSize: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.MeasurementConstraintsData // check the non changing items @@ -132,68 +131,68 @@ func TestMeasurementConstraintsListDataType_Update(t *testing.T) { } func TestMeasurementDescriptionListDataType_Update(t *testing.T) { - sut := model.MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + sut := MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []MeasurementDescriptionDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + MeasurementId: util.Ptr(MeasurementIdType(0)), + ScopeType: util.Ptr(ScopeTypeTypeACCurrent), }, { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + MeasurementId: util.Ptr(MeasurementIdType(1)), + ScopeType: util.Ptr(ScopeTypeTypeACCurrent), }, }, } - newData := model.MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + newData := MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []MeasurementDescriptionDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + MeasurementId: util.Ptr(MeasurementIdType(1)), + ScopeType: util.Ptr(ScopeTypeTypeACPower), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.MeasurementDescriptionData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.MeasurementId)) - assert.Equal(t, model.ScopeTypeTypeACCurrent, *item1.ScopeType) + assert.Equal(t, ScopeTypeTypeACCurrent, *item1.ScopeType) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.MeasurementId)) - assert.Equal(t, model.ScopeTypeTypeACPower, *item2.ScopeType) + assert.Equal(t, ScopeTypeTypeACPower, *item2.ScopeType) } func TestMeasurementThresholdRelationListDataType_Update(t *testing.T) { - sut := model.MeasurementThresholdRelationListDataType{ - MeasurementThresholdRelationData: []model.MeasurementThresholdRelationDataType{ + sut := MeasurementThresholdRelationListDataType{ + MeasurementThresholdRelationData: []MeasurementThresholdRelationDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ThresholdId: []model.ThresholdIdType{model.ThresholdIdType(0)}, + MeasurementId: util.Ptr(MeasurementIdType(0)), + ThresholdId: []ThresholdIdType{ThresholdIdType(0)}, }, { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ThresholdId: []model.ThresholdIdType{model.ThresholdIdType(0)}, + MeasurementId: util.Ptr(MeasurementIdType(1)), + ThresholdId: []ThresholdIdType{ThresholdIdType(0)}, }, }, } - newData := model.MeasurementThresholdRelationListDataType{ - MeasurementThresholdRelationData: []model.MeasurementThresholdRelationDataType{ + newData := MeasurementThresholdRelationListDataType{ + MeasurementThresholdRelationData: []MeasurementThresholdRelationDataType{ { - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - ThresholdId: []model.ThresholdIdType{model.ThresholdIdType(1)}, + MeasurementId: util.Ptr(MeasurementIdType(1)), + ThresholdId: []ThresholdIdType{ThresholdIdType(1)}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.MeasurementThresholdRelationData // check the non changing items diff --git a/spine/model/messaging_additions_test.go b/spine/model/messaging_additions_test.go index c2c5d7cb..7cf3f884 100644 --- a/spine/model/messaging_additions_test.go +++ b/spine/model/messaging_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestMessagingListDataType_Update(t *testing.T) { - sut := model.MessagingListDataType{ - MessagingData: []model.MessagingDataType{ + sut := MessagingListDataType{ + MessagingData: []MessagingDataType{ { - MessagingNumber: util.Ptr(model.MessagingNumberType(0)), - Text: util.Ptr(model.MessagingDataTextType("old")), + MessagingNumber: util.Ptr(MessagingNumberType(0)), + Text: util.Ptr(MessagingDataTextType("old")), }, { - MessagingNumber: util.Ptr(model.MessagingNumberType(1)), - Text: util.Ptr(model.MessagingDataTextType("old")), + MessagingNumber: util.Ptr(MessagingNumberType(1)), + Text: util.Ptr(MessagingDataTextType("old")), }, }, } - newData := model.MessagingListDataType{ - MessagingData: []model.MessagingDataType{ + newData := MessagingListDataType{ + MessagingData: []MessagingDataType{ { - MessagingNumber: util.Ptr(model.MessagingNumberType(1)), - Text: util.Ptr(model.MessagingDataTextType("new")), + MessagingNumber: util.Ptr(MessagingNumberType(1)), + Text: util.Ptr(MessagingDataTextType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.MessagingData // check the non changing items diff --git a/spine/model/operatingconstraints_additions_test.go b/spine/model/operatingconstraints_additions_test.go index c9a715a2..413b0dba 100644 --- a/spine/model/operatingconstraints_additions_test.go +++ b/spine/model/operatingconstraints_additions_test.go @@ -1,39 +1,38 @@ -package model_test +package model import ( "testing" "time" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestOperatingConstraintsInterruptListDataType_Update(t *testing.T) { - sut := model.OperatingConstraintsInterruptListDataType{ - OperatingConstraintsInterruptData: []model.OperatingConstraintsInterruptDataType{ + sut := OperatingConstraintsInterruptListDataType{ + OperatingConstraintsInterruptData: []OperatingConstraintsInterruptDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), + SequenceId: util.Ptr(PowerSequenceIdType(0)), IsPausable: util.Ptr(false), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), + SequenceId: util.Ptr(PowerSequenceIdType(1)), IsPausable: util.Ptr(false), }, }, } - newData := model.OperatingConstraintsInterruptListDataType{ - OperatingConstraintsInterruptData: []model.OperatingConstraintsInterruptDataType{ + newData := OperatingConstraintsInterruptListDataType{ + OperatingConstraintsInterruptData: []OperatingConstraintsInterruptDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), + SequenceId: util.Ptr(PowerSequenceIdType(1)), IsPausable: util.Ptr(true), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.OperatingConstraintsInterruptData // check the non changing items @@ -48,30 +47,30 @@ func TestOperatingConstraintsInterruptListDataType_Update(t *testing.T) { } func TestOperatingConstraintsDurationListDataType_Update(t *testing.T) { - sut := model.OperatingConstraintsDurationListDataType{ - OperatingConstraintsDurationData: []model.OperatingConstraintsDurationDataType{ + sut := OperatingConstraintsDurationListDataType{ + OperatingConstraintsDurationData: []OperatingConstraintsDurationDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - ActiveDurationMin: model.NewDurationType(1 * time.Second), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + ActiveDurationMin: NewDurationType(1 * time.Second), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - ActiveDurationMin: model.NewDurationType(1 * time.Second), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + ActiveDurationMin: NewDurationType(1 * time.Second), }, }, } - newData := model.OperatingConstraintsDurationListDataType{ - OperatingConstraintsDurationData: []model.OperatingConstraintsDurationDataType{ + newData := OperatingConstraintsDurationListDataType{ + OperatingConstraintsDurationData: []OperatingConstraintsDurationDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - ActiveDurationMin: model.NewDurationType(10 * time.Second), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + ActiveDurationMin: NewDurationType(10 * time.Second), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.OperatingConstraintsDurationData // check the non changing items @@ -88,68 +87,68 @@ func TestOperatingConstraintsDurationListDataType_Update(t *testing.T) { } func TestOperatingConstraintsPowerDescriptionListDataType_Update(t *testing.T) { - sut := model.OperatingConstraintsPowerDescriptionListDataType{ - OperatingConstraintsPowerDescriptionData: []model.OperatingConstraintsPowerDescriptionDataType{ + sut := OperatingConstraintsPowerDescriptionListDataType{ + OperatingConstraintsPowerDescriptionData: []OperatingConstraintsPowerDescriptionDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeConsume), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeConsume), }, }, } - newData := model.OperatingConstraintsPowerDescriptionListDataType{ - OperatingConstraintsPowerDescriptionData: []model.OperatingConstraintsPowerDescriptionDataType{ + newData := OperatingConstraintsPowerDescriptionListDataType{ + OperatingConstraintsPowerDescriptionData: []OperatingConstraintsPowerDescriptionDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeProduce), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeProduce), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.OperatingConstraintsPowerDescriptionData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, model.EnergyDirectionTypeConsume, *item1.PositiveEnergyDirection) + assert.Equal(t, EnergyDirectionTypeConsume, *item1.PositiveEnergyDirection) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, model.EnergyDirectionTypeProduce, *item2.PositiveEnergyDirection) + assert.Equal(t, EnergyDirectionTypeProduce, *item2.PositiveEnergyDirection) } func TestOperatingConstraintsPowerRangeListDataType_Update(t *testing.T) { - sut := model.OperatingConstraintsPowerRangeListDataType{ - OperatingConstraintsPowerRangeData: []model.OperatingConstraintsPowerRangeDataType{ + sut := OperatingConstraintsPowerRangeListDataType{ + OperatingConstraintsPowerRangeData: []OperatingConstraintsPowerRangeDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - PowerMin: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + PowerMin: NewScaledNumberType(1), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - PowerMin: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + PowerMin: NewScaledNumberType(1), }, }, } - newData := model.OperatingConstraintsPowerRangeListDataType{ - OperatingConstraintsPowerRangeData: []model.OperatingConstraintsPowerRangeDataType{ + newData := OperatingConstraintsPowerRangeListDataType{ + OperatingConstraintsPowerRangeData: []OperatingConstraintsPowerRangeDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - PowerMin: model.NewScaledNumberType(10), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + PowerMin: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.OperatingConstraintsPowerRangeData // check the non changing items @@ -164,30 +163,30 @@ func TestOperatingConstraintsPowerRangeListDataType_Update(t *testing.T) { } func TestOperatingConstraintsPowerLevelListDataType_Update(t *testing.T) { - sut := model.OperatingConstraintsPowerLevelListDataType{ - OperatingConstraintsPowerLevelData: []model.OperatingConstraintsPowerLevelDataType{ + sut := OperatingConstraintsPowerLevelListDataType{ + OperatingConstraintsPowerLevelData: []OperatingConstraintsPowerLevelDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - Power: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + Power: NewScaledNumberType(1), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - Power: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + Power: NewScaledNumberType(1), }, }, } - newData := model.OperatingConstraintsPowerLevelListDataType{ - OperatingConstraintsPowerLevelData: []model.OperatingConstraintsPowerLevelDataType{ + newData := OperatingConstraintsPowerLevelListDataType{ + OperatingConstraintsPowerLevelData: []OperatingConstraintsPowerLevelDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - Power: model.NewScaledNumberType(10), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + Power: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.OperatingConstraintsPowerLevelData // check the non changing items @@ -202,30 +201,30 @@ func TestOperatingConstraintsPowerLevelListDataType_Update(t *testing.T) { } func TestOperatingConstraintsResumeImplicationListDataType_Update(t *testing.T) { - sut := model.OperatingConstraintsResumeImplicationListDataType{ - OperatingConstraintsResumeImplicationData: []model.OperatingConstraintsResumeImplicationDataType{ + sut := OperatingConstraintsResumeImplicationListDataType{ + OperatingConstraintsResumeImplicationData: []OperatingConstraintsResumeImplicationDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - ResumeEnergyEstimated: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + ResumeEnergyEstimated: NewScaledNumberType(1), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - ResumeEnergyEstimated: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + ResumeEnergyEstimated: NewScaledNumberType(1), }, }, } - newData := model.OperatingConstraintsResumeImplicationListDataType{ - OperatingConstraintsResumeImplicationData: []model.OperatingConstraintsResumeImplicationDataType{ + newData := OperatingConstraintsResumeImplicationListDataType{ + OperatingConstraintsResumeImplicationData: []OperatingConstraintsResumeImplicationDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - ResumeEnergyEstimated: model.NewScaledNumberType(10), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + ResumeEnergyEstimated: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.OperatingConstraintsResumeImplicationData // check the non changing items diff --git a/spine/model/powersequences_additions_test.go b/spine/model/powersequences_additions_test.go index cbc4c63c..25211bad 100644 --- a/spine/model/powersequences_additions_test.go +++ b/spine/model/powersequences_additions_test.go @@ -1,39 +1,38 @@ -package model_test +package model import ( "testing" "time" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestPowerTimeSlotScheduleListDataType_Update(t *testing.T) { - sut := model.PowerTimeSlotScheduleListDataType{ - PowerTimeSlotScheduleData: []model.PowerTimeSlotScheduleDataType{ + sut := PowerTimeSlotScheduleListDataType{ + PowerTimeSlotScheduleData: []PowerTimeSlotScheduleDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), + SequenceId: util.Ptr(PowerSequenceIdType(0)), SlotActivated: util.Ptr(false), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), + SequenceId: util.Ptr(PowerSequenceIdType(1)), SlotActivated: util.Ptr(false), }, }, } - newData := model.PowerTimeSlotScheduleListDataType{ - PowerTimeSlotScheduleData: []model.PowerTimeSlotScheduleDataType{ + newData := PowerTimeSlotScheduleListDataType{ + PowerTimeSlotScheduleData: []PowerTimeSlotScheduleDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), + SequenceId: util.Ptr(PowerSequenceIdType(1)), SlotActivated: util.Ptr(true), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerTimeSlotScheduleData // check the non changing items @@ -48,30 +47,30 @@ func TestPowerTimeSlotScheduleListDataType_Update(t *testing.T) { } func TestPowerTimeSlotValueListDataType_Update(t *testing.T) { - sut := model.PowerTimeSlotValueListDataType{ - PowerTimeSlotValueData: []model.PowerTimeSlotValueDataType{ + sut := PowerTimeSlotValueListDataType{ + PowerTimeSlotValueData: []PowerTimeSlotValueDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - Value: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + Value: NewScaledNumberType(1), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - Value: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + Value: NewScaledNumberType(1), }, }, } - newData := model.PowerTimeSlotValueListDataType{ - PowerTimeSlotValueData: []model.PowerTimeSlotValueDataType{ + newData := PowerTimeSlotValueListDataType{ + PowerTimeSlotValueData: []PowerTimeSlotValueDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - Value: model.NewScaledNumberType(10), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + Value: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerTimeSlotValueData // check the non changing items @@ -86,30 +85,30 @@ func TestPowerTimeSlotValueListDataType_Update(t *testing.T) { } func TestPowerTimeSlotScheduleConstraintsListDataType_Update(t *testing.T) { - sut := model.PowerTimeSlotScheduleConstraintsListDataType{ - PowerTimeSlotScheduleConstraintsData: []model.PowerTimeSlotScheduleConstraintsDataType{ + sut := PowerTimeSlotScheduleConstraintsListDataType{ + PowerTimeSlotScheduleConstraintsData: []PowerTimeSlotScheduleConstraintsDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - MinDuration: model.NewDurationType(1 * time.Second), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + MinDuration: NewDurationType(1 * time.Second), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - MinDuration: model.NewDurationType(1 * time.Second), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + MinDuration: NewDurationType(1 * time.Second), }, }, } - newData := model.PowerTimeSlotScheduleConstraintsListDataType{ - PowerTimeSlotScheduleConstraintsData: []model.PowerTimeSlotScheduleConstraintsDataType{ + newData := PowerTimeSlotScheduleConstraintsListDataType{ + PowerTimeSlotScheduleConstraintsData: []PowerTimeSlotScheduleConstraintsDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - MinDuration: model.NewDurationType(10 * time.Second), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + MinDuration: NewDurationType(10 * time.Second), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerTimeSlotScheduleConstraintsData // check the non changing items @@ -126,30 +125,30 @@ func TestPowerTimeSlotScheduleConstraintsListDataType_Update(t *testing.T) { } func TestPowerSequenceAlternativesRelationListDataType_Update(t *testing.T) { - sut := model.PowerSequenceAlternativesRelationListDataType{ - PowerSequenceAlternativesRelationData: []model.PowerSequenceAlternativesRelationDataType{ + sut := PowerSequenceAlternativesRelationListDataType{ + PowerSequenceAlternativesRelationData: []PowerSequenceAlternativesRelationDataType{ { - AlternativeId: util.Ptr(model.AlternativesIdType(0)), - SequenceId: []model.PowerSequenceIdType{0}, + AlternativeId: util.Ptr(AlternativesIdType(0)), + SequenceId: []PowerSequenceIdType{0}, }, { - AlternativeId: util.Ptr(model.AlternativesIdType(1)), - SequenceId: []model.PowerSequenceIdType{0}, + AlternativeId: util.Ptr(AlternativesIdType(1)), + SequenceId: []PowerSequenceIdType{0}, }, }, } - newData := model.PowerSequenceAlternativesRelationListDataType{ - PowerSequenceAlternativesRelationData: []model.PowerSequenceAlternativesRelationDataType{ + newData := PowerSequenceAlternativesRelationListDataType{ + PowerSequenceAlternativesRelationData: []PowerSequenceAlternativesRelationDataType{ { - AlternativeId: util.Ptr(model.AlternativesIdType(1)), - SequenceId: []model.PowerSequenceIdType{1}, + AlternativeId: util.Ptr(AlternativesIdType(1)), + SequenceId: []PowerSequenceIdType{1}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerSequenceAlternativesRelationData // check the non changing items @@ -164,106 +163,106 @@ func TestPowerSequenceAlternativesRelationListDataType_Update(t *testing.T) { } func TestPowerSequenceDescriptionListDataType_Update(t *testing.T) { - sut := model.PowerSequenceDescriptionListDataType{ - PowerSequenceDescriptionData: []model.PowerSequenceDescriptionDataType{ + sut := PowerSequenceDescriptionListDataType{ + PowerSequenceDescriptionData: []PowerSequenceDescriptionDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeConsume), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeConsume), }, }, } - newData := model.PowerSequenceDescriptionListDataType{ - PowerSequenceDescriptionData: []model.PowerSequenceDescriptionDataType{ + newData := PowerSequenceDescriptionListDataType{ + PowerSequenceDescriptionData: []PowerSequenceDescriptionDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeProduce), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeProduce), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerSequenceDescriptionData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, model.EnergyDirectionTypeConsume, *item1.PositiveEnergyDirection) + assert.Equal(t, EnergyDirectionTypeConsume, *item1.PositiveEnergyDirection) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, model.EnergyDirectionTypeProduce, *item2.PositiveEnergyDirection) + assert.Equal(t, EnergyDirectionTypeProduce, *item2.PositiveEnergyDirection) } func TestPowerSequenceStateListDataType_Update(t *testing.T) { - sut := model.PowerSequenceStateListDataType{ - PowerSequenceStateData: []model.PowerSequenceStateDataType{ + sut := PowerSequenceStateListDataType{ + PowerSequenceStateData: []PowerSequenceStateDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - State: util.Ptr(model.PowerSequenceStateTypeRunning), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + State: util.Ptr(PowerSequenceStateTypeRunning), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - State: util.Ptr(model.PowerSequenceStateTypeRunning), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + State: util.Ptr(PowerSequenceStateTypeRunning), }, }, } - newData := model.PowerSequenceStateListDataType{ - PowerSequenceStateData: []model.PowerSequenceStateDataType{ + newData := PowerSequenceStateListDataType{ + PowerSequenceStateData: []PowerSequenceStateDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - State: util.Ptr(model.PowerSequenceStateTypeCompleted), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + State: util.Ptr(PowerSequenceStateTypeCompleted), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerSequenceStateData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, model.PowerSequenceStateTypeRunning, *item1.State) + assert.Equal(t, PowerSequenceStateTypeRunning, *item1.State) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, model.PowerSequenceStateTypeCompleted, *item2.State) + assert.Equal(t, PowerSequenceStateTypeCompleted, *item2.State) } func TestPowerSequenceScheduleListDataType_Update(t *testing.T) { - sut := model.PowerSequenceScheduleListDataType{ - PowerSequenceScheduleData: []model.PowerSequenceScheduleDataType{ + sut := PowerSequenceScheduleListDataType{ + PowerSequenceScheduleData: []PowerSequenceScheduleDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - EndTime: model.NewAbsoluteOrRelativeTimeType("PT2H"), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + EndTime: NewAbsoluteOrRelativeTimeType("PT2H"), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - EndTime: model.NewAbsoluteOrRelativeTimeType("PT2H"), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + EndTime: NewAbsoluteOrRelativeTimeType("PT2H"), }, }, } - newData := model.PowerSequenceScheduleListDataType{ - PowerSequenceScheduleData: []model.PowerSequenceScheduleDataType{ + newData := PowerSequenceScheduleListDataType{ + PowerSequenceScheduleData: []PowerSequenceScheduleDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - EndTime: model.NewAbsoluteOrRelativeTimeType("PT4H"), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + EndTime: NewAbsoluteOrRelativeTimeType("PT4H"), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerSequenceScheduleData // check the non changing items @@ -278,30 +277,30 @@ func TestPowerSequenceScheduleListDataType_Update(t *testing.T) { } func TestPowerSequenceScheduleConstraintsListDataType_Update(t *testing.T) { - sut := model.PowerSequenceScheduleConstraintsListDataType{ - PowerSequenceScheduleConstraintsData: []model.PowerSequenceScheduleConstraintsDataType{ + sut := PowerSequenceScheduleConstraintsListDataType{ + PowerSequenceScheduleConstraintsData: []PowerSequenceScheduleConstraintsDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - EarliestEndTime: model.NewAbsoluteOrRelativeTimeType("PT2H"), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + EarliestEndTime: NewAbsoluteOrRelativeTimeType("PT2H"), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - EarliestEndTime: model.NewAbsoluteOrRelativeTimeType("PT2H"), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + EarliestEndTime: NewAbsoluteOrRelativeTimeType("PT2H"), }, }, } - newData := model.PowerSequenceScheduleConstraintsListDataType{ - PowerSequenceScheduleConstraintsData: []model.PowerSequenceScheduleConstraintsDataType{ + newData := PowerSequenceScheduleConstraintsListDataType{ + PowerSequenceScheduleConstraintsData: []PowerSequenceScheduleConstraintsDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - EarliestEndTime: model.NewAbsoluteOrRelativeTimeType("PT4H"), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + EarliestEndTime: NewAbsoluteOrRelativeTimeType("PT4H"), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerSequenceScheduleConstraintsData // check the non changing items @@ -316,30 +315,30 @@ func TestPowerSequenceScheduleConstraintsListDataType_Update(t *testing.T) { } func TestPowerSequencePriceListDataType_Update(t *testing.T) { - sut := model.PowerSequencePriceListDataType{ - PowerSequencePriceData: []model.PowerSequencePriceDataType{ + sut := PowerSequencePriceListDataType{ + PowerSequencePriceData: []PowerSequencePriceDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), - Price: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(0)), + Price: NewScaledNumberType(1), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - Price: model.NewScaledNumberType(1), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + Price: NewScaledNumberType(1), }, }, } - newData := model.PowerSequencePriceListDataType{ - PowerSequencePriceData: []model.PowerSequencePriceDataType{ + newData := PowerSequencePriceListDataType{ + PowerSequencePriceData: []PowerSequencePriceDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), - Price: model.NewScaledNumberType(10), + SequenceId: util.Ptr(PowerSequenceIdType(1)), + Price: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerSequencePriceData // check the non changing items @@ -354,30 +353,30 @@ func TestPowerSequencePriceListDataType_Update(t *testing.T) { } func TestPowerSequenceSchedulePreferenceListDataType_Update(t *testing.T) { - sut := model.PowerSequenceSchedulePreferenceListDataType{ - PowerSequenceSchedulePreferenceData: []model.PowerSequenceSchedulePreferenceDataType{ + sut := PowerSequenceSchedulePreferenceListDataType{ + PowerSequenceSchedulePreferenceData: []PowerSequenceSchedulePreferenceDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(0)), + SequenceId: util.Ptr(PowerSequenceIdType(0)), Cheapest: util.Ptr(false), }, { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), + SequenceId: util.Ptr(PowerSequenceIdType(1)), Cheapest: util.Ptr(false), }, }, } - newData := model.PowerSequenceSchedulePreferenceListDataType{ - PowerSequenceSchedulePreferenceData: []model.PowerSequenceSchedulePreferenceDataType{ + newData := PowerSequenceSchedulePreferenceListDataType{ + PowerSequenceSchedulePreferenceData: []PowerSequenceSchedulePreferenceDataType{ { - SequenceId: util.Ptr(model.PowerSequenceIdType(1)), + SequenceId: util.Ptr(PowerSequenceIdType(1)), Cheapest: util.Ptr(true), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.PowerSequenceSchedulePreferenceData // check the non changing items diff --git a/spine/model/setpoint_additions_test.go b/spine/model/setpoint_additions_test.go index dbcaefbb..463273d2 100644 --- a/spine/model/setpoint_additions_test.go +++ b/spine/model/setpoint_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestSetpointListDataType_Update(t *testing.T) { - sut := model.SetpointListDataType{ - SetpointData: []model.SetpointDataType{ + sut := SetpointListDataType{ + SetpointData: []SetpointDataType{ { - SetpointId: util.Ptr(model.SetpointIdType(0)), - Value: model.NewScaledNumberType(1), + SetpointId: util.Ptr(SetpointIdType(0)), + Value: NewScaledNumberType(1), }, { - SetpointId: util.Ptr(model.SetpointIdType(1)), - Value: model.NewScaledNumberType(1), + SetpointId: util.Ptr(SetpointIdType(1)), + Value: NewScaledNumberType(1), }, }, } - newData := model.SetpointListDataType{ - SetpointData: []model.SetpointDataType{ + newData := SetpointListDataType{ + SetpointData: []SetpointDataType{ { - SetpointId: util.Ptr(model.SetpointIdType(1)), - Value: model.NewScaledNumberType(10), + SetpointId: util.Ptr(SetpointIdType(1)), + Value: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.SetpointData // check the non changing items @@ -47,30 +46,30 @@ func TestSetpointListDataType_Update(t *testing.T) { } func TestSetpointDescriptionListDataType_Update(t *testing.T) { - sut := model.SetpointDescriptionListDataType{ - SetpointDescriptionData: []model.SetpointDescriptionDataType{ + sut := SetpointDescriptionListDataType{ + SetpointDescriptionData: []SetpointDescriptionDataType{ { - SetpointId: util.Ptr(model.SetpointIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + SetpointId: util.Ptr(SetpointIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - SetpointId: util.Ptr(model.SetpointIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + SetpointId: util.Ptr(SetpointIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.SetpointDescriptionListDataType{ - SetpointDescriptionData: []model.SetpointDescriptionDataType{ + newData := SetpointDescriptionListDataType{ + SetpointDescriptionData: []SetpointDescriptionDataType{ { - SetpointId: util.Ptr(model.SetpointIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + SetpointId: util.Ptr(SetpointIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.SetpointDescriptionData // check the non changing items diff --git a/spine/model/subscriptionmanagement_additions_test.go b/spine/model/subscriptionmanagement_additions_test.go index 0f783041..38ae2ce7 100644 --- a/spine/model/subscriptionmanagement_additions_test.go +++ b/spine/model/subscriptionmanagement_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestSubscriptionManagementEntryListDataType_Update(t *testing.T) { - sut := model.SubscriptionManagementEntryListDataType{ - SubscriptionManagementEntryData: []model.SubscriptionManagementEntryDataType{ + sut := SubscriptionManagementEntryListDataType{ + SubscriptionManagementEntryData: []SubscriptionManagementEntryDataType{ { - SubscriptionId: util.Ptr(model.SubscriptionIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + SubscriptionId: util.Ptr(SubscriptionIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - SubscriptionId: util.Ptr(model.SubscriptionIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + SubscriptionId: util.Ptr(SubscriptionIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.SubscriptionManagementEntryListDataType{ - SubscriptionManagementEntryData: []model.SubscriptionManagementEntryDataType{ + newData := SubscriptionManagementEntryListDataType{ + SubscriptionManagementEntryData: []SubscriptionManagementEntryDataType{ { - SubscriptionId: util.Ptr(model.SubscriptionIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + SubscriptionId: util.Ptr(SubscriptionIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.SubscriptionManagementEntryData // check the non changing items diff --git a/spine/model/supplyconditions_additions_test.go b/spine/model/supplyconditions_additions_test.go index 615b7de2..d98f7af9 100644 --- a/spine/model/supplyconditions_additions_test.go +++ b/spine/model/supplyconditions_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestSupplyConditionListDataType_Update(t *testing.T) { - sut := model.SupplyConditionListDataType{ - SupplyConditionData: []model.SupplyConditionDataType{ + sut := SupplyConditionListDataType{ + SupplyConditionData: []SupplyConditionDataType{ { - ConditionId: util.Ptr(model.ConditionIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + ConditionId: util.Ptr(ConditionIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - ConditionId: util.Ptr(model.ConditionIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + ConditionId: util.Ptr(ConditionIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.SupplyConditionListDataType{ - SupplyConditionData: []model.SupplyConditionDataType{ + newData := SupplyConditionListDataType{ + SupplyConditionData: []SupplyConditionDataType{ { - ConditionId: util.Ptr(model.ConditionIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + ConditionId: util.Ptr(ConditionIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.SupplyConditionData // check the non changing items @@ -47,30 +46,30 @@ func TestSupplyConditionListDataType_Update(t *testing.T) { } func TestSupplyConditionDescriptionListDataType_Update(t *testing.T) { - sut := model.SupplyConditionDescriptionListDataType{ - SupplyConditionDescriptionData: []model.SupplyConditionDescriptionDataType{ + sut := SupplyConditionDescriptionListDataType{ + SupplyConditionDescriptionData: []SupplyConditionDescriptionDataType{ { - ConditionId: util.Ptr(model.ConditionIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + ConditionId: util.Ptr(ConditionIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - ConditionId: util.Ptr(model.ConditionIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + ConditionId: util.Ptr(ConditionIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.SupplyConditionDescriptionListDataType{ - SupplyConditionDescriptionData: []model.SupplyConditionDescriptionDataType{ + newData := SupplyConditionDescriptionListDataType{ + SupplyConditionDescriptionData: []SupplyConditionDescriptionDataType{ { - ConditionId: util.Ptr(model.ConditionIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + ConditionId: util.Ptr(ConditionIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.SupplyConditionDescriptionData // check the non changing items @@ -85,30 +84,30 @@ func TestSupplyConditionDescriptionListDataType_Update(t *testing.T) { } func TestSupplyConditionThresholdRelationListDataType_Update(t *testing.T) { - sut := model.SupplyConditionThresholdRelationListDataType{ - SupplyConditionThresholdRelationData: []model.SupplyConditionThresholdRelationDataType{ + sut := SupplyConditionThresholdRelationListDataType{ + SupplyConditionThresholdRelationData: []SupplyConditionThresholdRelationDataType{ { - ConditionId: util.Ptr(model.ConditionIdType(0)), - ThresholdId: []model.ThresholdIdType{0}, + ConditionId: util.Ptr(ConditionIdType(0)), + ThresholdId: []ThresholdIdType{0}, }, { - ConditionId: util.Ptr(model.ConditionIdType(1)), - ThresholdId: []model.ThresholdIdType{0}, + ConditionId: util.Ptr(ConditionIdType(1)), + ThresholdId: []ThresholdIdType{0}, }, }, } - newData := model.SupplyConditionThresholdRelationListDataType{ - SupplyConditionThresholdRelationData: []model.SupplyConditionThresholdRelationDataType{ + newData := SupplyConditionThresholdRelationListDataType{ + SupplyConditionThresholdRelationData: []SupplyConditionThresholdRelationDataType{ { - ConditionId: util.Ptr(model.ConditionIdType(1)), - ThresholdId: []model.ThresholdIdType{1}, + ConditionId: util.Ptr(ConditionIdType(1)), + ThresholdId: []ThresholdIdType{1}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.SupplyConditionThresholdRelationData // check the non changing items diff --git a/spine/model/tariffinformation_additions_test.go b/spine/model/tariffinformation_additions_test.go index e590d5dd..776168b4 100644 --- a/spine/model/tariffinformation_additions_test.go +++ b/spine/model/tariffinformation_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestTariffListDataType_Update(t *testing.T) { - sut := model.TariffListDataType{ - TariffData: []model.TariffDataType{ + sut := TariffListDataType{ + TariffData: []TariffDataType{ { - TariffId: util.Ptr(model.TariffIdType(0)), - ActiveTierId: []model.TierIdType{0}, + TariffId: util.Ptr(TariffIdType(0)), + ActiveTierId: []TierIdType{0}, }, { - TariffId: util.Ptr(model.TariffIdType(1)), - ActiveTierId: []model.TierIdType{0}, + TariffId: util.Ptr(TariffIdType(1)), + ActiveTierId: []TierIdType{0}, }, }, } - newData := model.TariffListDataType{ - TariffData: []model.TariffDataType{ + newData := TariffListDataType{ + TariffData: []TariffDataType{ { - TariffId: util.Ptr(model.TariffIdType(1)), - ActiveTierId: []model.TierIdType{1}, + TariffId: util.Ptr(TariffIdType(1)), + ActiveTierId: []TierIdType{1}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TariffData // check the non changing items @@ -47,30 +46,30 @@ func TestTariffListDataType_Update(t *testing.T) { } func TestTariffTierRelationListDataType_Update(t *testing.T) { - sut := model.TariffTierRelationListDataType{ - TariffTierRelationData: []model.TariffTierRelationDataType{ + sut := TariffTierRelationListDataType{ + TariffTierRelationData: []TariffTierRelationDataType{ { - TariffId: util.Ptr(model.TariffIdType(0)), - TierId: []model.TierIdType{0}, + TariffId: util.Ptr(TariffIdType(0)), + TierId: []TierIdType{0}, }, { - TariffId: util.Ptr(model.TariffIdType(1)), - TierId: []model.TierIdType{0}, + TariffId: util.Ptr(TariffIdType(1)), + TierId: []TierIdType{0}, }, }, } - newData := model.TariffTierRelationListDataType{ - TariffTierRelationData: []model.TariffTierRelationDataType{ + newData := TariffTierRelationListDataType{ + TariffTierRelationData: []TariffTierRelationDataType{ { - TariffId: util.Ptr(model.TariffIdType(1)), - TierId: []model.TierIdType{1}, + TariffId: util.Ptr(TariffIdType(1)), + TierId: []TierIdType{1}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TariffTierRelationData // check the non changing items @@ -85,30 +84,30 @@ func TestTariffTierRelationListDataType_Update(t *testing.T) { } func TestTariffBoundaryRelationListDataType_Update(t *testing.T) { - sut := model.TariffBoundaryRelationListDataType{ - TariffBoundaryRelationData: []model.TariffBoundaryRelationDataType{ + sut := TariffBoundaryRelationListDataType{ + TariffBoundaryRelationData: []TariffBoundaryRelationDataType{ { - TariffId: util.Ptr(model.TariffIdType(0)), - BoundaryId: []model.TierBoundaryIdType{0}, + TariffId: util.Ptr(TariffIdType(0)), + BoundaryId: []TierBoundaryIdType{0}, }, { - TariffId: util.Ptr(model.TariffIdType(1)), - BoundaryId: []model.TierBoundaryIdType{0}, + TariffId: util.Ptr(TariffIdType(1)), + BoundaryId: []TierBoundaryIdType{0}, }, }, } - newData := model.TariffBoundaryRelationListDataType{ - TariffBoundaryRelationData: []model.TariffBoundaryRelationDataType{ + newData := TariffBoundaryRelationListDataType{ + TariffBoundaryRelationData: []TariffBoundaryRelationDataType{ { - TariffId: util.Ptr(model.TariffIdType(1)), - BoundaryId: []model.TierBoundaryIdType{1}, + TariffId: util.Ptr(TariffIdType(1)), + BoundaryId: []TierBoundaryIdType{1}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TariffBoundaryRelationData // check the non changing items @@ -123,30 +122,30 @@ func TestTariffBoundaryRelationListDataType_Update(t *testing.T) { } func TestTariffDescriptionListDataType_Update(t *testing.T) { - sut := model.TariffDescriptionListDataType{ - TariffDescriptionData: []model.TariffDescriptionDataType{ + sut := TariffDescriptionListDataType{ + TariffDescriptionData: []TariffDescriptionDataType{ { - TariffId: util.Ptr(model.TariffIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + TariffId: util.Ptr(TariffIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - TariffId: util.Ptr(model.TariffIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + TariffId: util.Ptr(TariffIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.TariffDescriptionListDataType{ - TariffDescriptionData: []model.TariffDescriptionDataType{ + newData := TariffDescriptionListDataType{ + TariffDescriptionData: []TariffDescriptionDataType{ { - TariffId: util.Ptr(model.TariffIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + TariffId: util.Ptr(TariffIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TariffDescriptionData // check the non changing items @@ -161,30 +160,30 @@ func TestTariffDescriptionListDataType_Update(t *testing.T) { } func TestTierBoundaryListDataType_Update(t *testing.T) { - sut := model.TierBoundaryListDataType{ - TierBoundaryData: []model.TierBoundaryDataType{ + sut := TierBoundaryListDataType{ + TierBoundaryData: []TierBoundaryDataType{ { - BoundaryId: util.Ptr(model.TierBoundaryIdType(0)), - LowerBoundaryValue: model.NewScaledNumberType(1), + BoundaryId: util.Ptr(TierBoundaryIdType(0)), + LowerBoundaryValue: NewScaledNumberType(1), }, { - BoundaryId: util.Ptr(model.TierBoundaryIdType(1)), - LowerBoundaryValue: model.NewScaledNumberType(1), + BoundaryId: util.Ptr(TierBoundaryIdType(1)), + LowerBoundaryValue: NewScaledNumberType(1), }, }, } - newData := model.TierBoundaryListDataType{ - TierBoundaryData: []model.TierBoundaryDataType{ + newData := TierBoundaryListDataType{ + TierBoundaryData: []TierBoundaryDataType{ { - BoundaryId: util.Ptr(model.TierBoundaryIdType(1)), - LowerBoundaryValue: model.NewScaledNumberType(10), + BoundaryId: util.Ptr(TierBoundaryIdType(1)), + LowerBoundaryValue: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TierBoundaryData // check the non changing items @@ -199,30 +198,30 @@ func TestTierBoundaryListDataType_Update(t *testing.T) { } func TestTierBoundaryDescriptionListDataType_Update(t *testing.T) { - sut := model.TierBoundaryDescriptionListDataType{ - TierBoundaryDescriptionData: []model.TierBoundaryDescriptionDataType{ + sut := TierBoundaryDescriptionListDataType{ + TierBoundaryDescriptionData: []TierBoundaryDescriptionDataType{ { - BoundaryId: util.Ptr(model.TierBoundaryIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + BoundaryId: util.Ptr(TierBoundaryIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - BoundaryId: util.Ptr(model.TierBoundaryIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + BoundaryId: util.Ptr(TierBoundaryIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.TierBoundaryDescriptionListDataType{ - TierBoundaryDescriptionData: []model.TierBoundaryDescriptionDataType{ + newData := TierBoundaryDescriptionListDataType{ + TierBoundaryDescriptionData: []TierBoundaryDescriptionDataType{ { - BoundaryId: util.Ptr(model.TierBoundaryIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + BoundaryId: util.Ptr(TierBoundaryIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TierBoundaryDescriptionData // check the non changing items @@ -237,30 +236,30 @@ func TestTierBoundaryDescriptionListDataType_Update(t *testing.T) { } func TestCommodityListDataType_Update(t *testing.T) { - sut := model.CommodityListDataType{ - CommodityData: []model.CommodityDataType{ + sut := CommodityListDataType{ + CommodityData: []CommodityDataType{ { - CommodityId: util.Ptr(model.CommodityIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + CommodityId: util.Ptr(CommodityIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - CommodityId: util.Ptr(model.CommodityIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + CommodityId: util.Ptr(CommodityIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.CommodityListDataType{ - CommodityData: []model.CommodityDataType{ + newData := CommodityListDataType{ + CommodityData: []CommodityDataType{ { - CommodityId: util.Ptr(model.CommodityIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + CommodityId: util.Ptr(CommodityIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.CommodityData // check the non changing items @@ -275,30 +274,30 @@ func TestCommodityListDataType_Update(t *testing.T) { } func TestTierListDataType_Update(t *testing.T) { - sut := model.TierListDataType{ - TierData: []model.TierDataType{ + sut := TierListDataType{ + TierData: []TierDataType{ { - TierId: util.Ptr(model.TierIdType(0)), - ActiveIncentiveId: []model.IncentiveIdType{0}, + TierId: util.Ptr(TierIdType(0)), + ActiveIncentiveId: []IncentiveIdType{0}, }, { - TierId: util.Ptr(model.TierIdType(1)), - ActiveIncentiveId: []model.IncentiveIdType{0}, + TierId: util.Ptr(TierIdType(1)), + ActiveIncentiveId: []IncentiveIdType{0}, }, }, } - newData := model.TierListDataType{ - TierData: []model.TierDataType{ + newData := TierListDataType{ + TierData: []TierDataType{ { - TierId: util.Ptr(model.TierIdType(1)), - ActiveIncentiveId: []model.IncentiveIdType{1}, + TierId: util.Ptr(TierIdType(1)), + ActiveIncentiveId: []IncentiveIdType{1}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TierData // check the non changing items @@ -313,30 +312,30 @@ func TestTierListDataType_Update(t *testing.T) { } func TestTierIncentiveRelationListDataType_Update(t *testing.T) { - sut := model.TierIncentiveRelationListDataType{ - TierIncentiveRelationData: []model.TierIncentiveRelationDataType{ + sut := TierIncentiveRelationListDataType{ + TierIncentiveRelationData: []TierIncentiveRelationDataType{ { - TierId: util.Ptr(model.TierIdType(0)), - IncentiveId: []model.IncentiveIdType{0}, + TierId: util.Ptr(TierIdType(0)), + IncentiveId: []IncentiveIdType{0}, }, { - TierId: util.Ptr(model.TierIdType(1)), - IncentiveId: []model.IncentiveIdType{0}, + TierId: util.Ptr(TierIdType(1)), + IncentiveId: []IncentiveIdType{0}, }, }, } - newData := model.TierIncentiveRelationListDataType{ - TierIncentiveRelationData: []model.TierIncentiveRelationDataType{ + newData := TierIncentiveRelationListDataType{ + TierIncentiveRelationData: []TierIncentiveRelationDataType{ { - TierId: util.Ptr(model.TierIdType(1)), - IncentiveId: []model.IncentiveIdType{1}, + TierId: util.Ptr(TierIdType(1)), + IncentiveId: []IncentiveIdType{1}, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TierIncentiveRelationData // check the non changing items @@ -351,30 +350,30 @@ func TestTierIncentiveRelationListDataType_Update(t *testing.T) { } func TestTierDescriptionListDataType_Update(t *testing.T) { - sut := model.TierDescriptionListDataType{ - TierDescriptionData: []model.TierDescriptionDataType{ + sut := TierDescriptionListDataType{ + TierDescriptionData: []TierDescriptionDataType{ { - TierId: util.Ptr(model.TierIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + TierId: util.Ptr(TierIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - TierId: util.Ptr(model.TierIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + TierId: util.Ptr(TierIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.TierDescriptionListDataType{ - TierDescriptionData: []model.TierDescriptionDataType{ + newData := TierDescriptionListDataType{ + TierDescriptionData: []TierDescriptionDataType{ { - TierId: util.Ptr(model.TierIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + TierId: util.Ptr(TierIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TierDescriptionData // check the non changing items @@ -389,30 +388,30 @@ func TestTierDescriptionListDataType_Update(t *testing.T) { } func TestIncentiveListDataType_Update(t *testing.T) { - sut := model.IncentiveListDataType{ - IncentiveData: []model.IncentiveDataType{ + sut := IncentiveListDataType{ + IncentiveData: []IncentiveDataType{ { - IncentiveId: util.Ptr(model.IncentiveIdType(0)), - Value: model.NewScaledNumberType(1), + IncentiveId: util.Ptr(IncentiveIdType(0)), + Value: NewScaledNumberType(1), }, { - IncentiveId: util.Ptr(model.IncentiveIdType(1)), - Value: model.NewScaledNumberType(1), + IncentiveId: util.Ptr(IncentiveIdType(1)), + Value: NewScaledNumberType(1), }, }, } - newData := model.IncentiveListDataType{ - IncentiveData: []model.IncentiveDataType{ + newData := IncentiveListDataType{ + IncentiveData: []IncentiveDataType{ { - IncentiveId: util.Ptr(model.IncentiveIdType(1)), - Value: model.NewScaledNumberType(10), + IncentiveId: util.Ptr(IncentiveIdType(1)), + Value: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.IncentiveData // check the non changing items @@ -427,30 +426,30 @@ func TestIncentiveListDataType_Update(t *testing.T) { } func TestIncentiveDescriptionListDataType_Update(t *testing.T) { - sut := model.IncentiveDescriptionListDataType{ - IncentiveDescriptionData: []model.IncentiveDescriptionDataType{ + sut := IncentiveDescriptionListDataType{ + IncentiveDescriptionData: []IncentiveDescriptionDataType{ { - IncentiveId: util.Ptr(model.IncentiveIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + IncentiveId: util.Ptr(IncentiveIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - IncentiveId: util.Ptr(model.IncentiveIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + IncentiveId: util.Ptr(IncentiveIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.IncentiveDescriptionListDataType{ - IncentiveDescriptionData: []model.IncentiveDescriptionDataType{ + newData := IncentiveDescriptionListDataType{ + IncentiveDescriptionData: []IncentiveDescriptionDataType{ { - IncentiveId: util.Ptr(model.IncentiveIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + IncentiveId: util.Ptr(IncentiveIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.IncentiveDescriptionData // check the non changing items diff --git a/spine/model/taskmanagement_additions_test.go b/spine/model/taskmanagement_additions_test.go index 39251682..25c88818 100644 --- a/spine/model/taskmanagement_additions_test.go +++ b/spine/model/taskmanagement_additions_test.go @@ -1,82 +1,81 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestTaskManagementJobListDataType_Update(t *testing.T) { - sut := model.TaskManagementJobListDataType{ - TaskManagementJobData: []model.TaskManagementJobDataType{ + sut := TaskManagementJobListDataType{ + TaskManagementJobData: []TaskManagementJobDataType{ { - JobId: util.Ptr(model.TaskManagementJobIdType(0)), - JobState: util.Ptr(model.TaskManagementJobStateTypeActive), + JobId: util.Ptr(TaskManagementJobIdType(0)), + JobState: util.Ptr(TaskManagementJobStateTypeActive), }, { - JobId: util.Ptr(model.TaskManagementJobIdType(1)), - JobState: util.Ptr(model.TaskManagementJobStateTypeActive), + JobId: util.Ptr(TaskManagementJobIdType(1)), + JobState: util.Ptr(TaskManagementJobStateTypeActive), }, }, } - newData := model.TaskManagementJobListDataType{ - TaskManagementJobData: []model.TaskManagementJobDataType{ + newData := TaskManagementJobListDataType{ + TaskManagementJobData: []TaskManagementJobDataType{ { - JobId: util.Ptr(model.TaskManagementJobIdType(1)), - JobState: util.Ptr(model.TaskManagementJobStateTypeCompleted), + JobId: util.Ptr(TaskManagementJobIdType(1)), + JobState: util.Ptr(TaskManagementJobStateTypeCompleted), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TaskManagementJobData // check the non changing items assert.Equal(t, 2, len(data)) item1 := data[0] assert.Equal(t, 0, int(*item1.JobId)) - assert.Equal(t, model.TaskManagementJobStateTypeActive, *item1.JobState) + assert.Equal(t, TaskManagementJobStateTypeActive, *item1.JobState) // check properties of updated item item2 := data[1] assert.Equal(t, 1, int(*item2.JobId)) - assert.Equal(t, model.TaskManagementJobStateTypeCompleted, *item2.JobState) + assert.Equal(t, TaskManagementJobStateTypeCompleted, *item2.JobState) } func TestTaskManagementJobRelationListDataType_Update(t *testing.T) { - sut := model.TaskManagementJobRelationListDataType{ - TaskManagementJobRelationData: []model.TaskManagementJobRelationDataType{ + sut := TaskManagementJobRelationListDataType{ + TaskManagementJobRelationData: []TaskManagementJobRelationDataType{ { - JobId: util.Ptr(model.TaskManagementJobIdType(0)), - LoadControlReleated: &model.TaskManagementLoadControlReleatedType{ - EventId: util.Ptr(model.LoadControlEventIdType(0)), + JobId: util.Ptr(TaskManagementJobIdType(0)), + LoadControlReleated: &TaskManagementLoadControlReleatedType{ + EventId: util.Ptr(LoadControlEventIdType(0)), }, }, { - JobId: util.Ptr(model.TaskManagementJobIdType(1)), - LoadControlReleated: &model.TaskManagementLoadControlReleatedType{ - EventId: util.Ptr(model.LoadControlEventIdType(0)), + JobId: util.Ptr(TaskManagementJobIdType(1)), + LoadControlReleated: &TaskManagementLoadControlReleatedType{ + EventId: util.Ptr(LoadControlEventIdType(0)), }, }, }, } - newData := model.TaskManagementJobRelationListDataType{ - TaskManagementJobRelationData: []model.TaskManagementJobRelationDataType{ + newData := TaskManagementJobRelationListDataType{ + TaskManagementJobRelationData: []TaskManagementJobRelationDataType{ { - JobId: util.Ptr(model.TaskManagementJobIdType(1)), - LoadControlReleated: &model.TaskManagementLoadControlReleatedType{ - EventId: util.Ptr(model.LoadControlEventIdType(1)), + JobId: util.Ptr(TaskManagementJobIdType(1)), + LoadControlReleated: &TaskManagementLoadControlReleatedType{ + EventId: util.Ptr(LoadControlEventIdType(1)), }, }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TaskManagementJobRelationData // check the non changing items @@ -91,30 +90,30 @@ func TestTaskManagementJobRelationListDataType_Update(t *testing.T) { } func TestTaskManagementJobDescriptionListDataType_Update(t *testing.T) { - sut := model.TaskManagementJobDescriptionListDataType{ - TaskManagementJobDescriptionData: []model.TaskManagementJobDescriptionDataType{ + sut := TaskManagementJobDescriptionListDataType{ + TaskManagementJobDescriptionData: []TaskManagementJobDescriptionDataType{ { - JobId: util.Ptr(model.TaskManagementJobIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + JobId: util.Ptr(TaskManagementJobIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - JobId: util.Ptr(model.TaskManagementJobIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + JobId: util.Ptr(TaskManagementJobIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.TaskManagementJobDescriptionListDataType{ - TaskManagementJobDescriptionData: []model.TaskManagementJobDescriptionDataType{ + newData := TaskManagementJobDescriptionListDataType{ + TaskManagementJobDescriptionData: []TaskManagementJobDescriptionDataType{ { - JobId: util.Ptr(model.TaskManagementJobIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + JobId: util.Ptr(TaskManagementJobIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TaskManagementJobDescriptionData // check the non changing items diff --git a/spine/model/threshold_additions_test.go b/spine/model/threshold_additions_test.go index 81662d77..01f92fa6 100644 --- a/spine/model/threshold_additions_test.go +++ b/spine/model/threshold_additions_test.go @@ -1,38 +1,37 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestThresholdListDataType_Update(t *testing.T) { - sut := model.ThresholdListDataType{ - ThresholdData: []model.ThresholdDataType{ + sut := ThresholdListDataType{ + ThresholdData: []ThresholdDataType{ { - ThresholdId: util.Ptr(model.ThresholdIdType(0)), - ThresholdValue: model.NewScaledNumberType(1), + ThresholdId: util.Ptr(ThresholdIdType(0)), + ThresholdValue: NewScaledNumberType(1), }, { - ThresholdId: util.Ptr(model.ThresholdIdType(1)), - ThresholdValue: model.NewScaledNumberType(1), + ThresholdId: util.Ptr(ThresholdIdType(1)), + ThresholdValue: NewScaledNumberType(1), }, }, } - newData := model.ThresholdListDataType{ - ThresholdData: []model.ThresholdDataType{ + newData := ThresholdListDataType{ + ThresholdData: []ThresholdDataType{ { - ThresholdId: util.Ptr(model.ThresholdIdType(1)), - ThresholdValue: model.NewScaledNumberType(10), + ThresholdId: util.Ptr(ThresholdIdType(1)), + ThresholdValue: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ThresholdData // check the non changing items @@ -47,30 +46,30 @@ func TestThresholdListDataType_Update(t *testing.T) { } func TestThresholdConstraintsListDataType_Update(t *testing.T) { - sut := model.ThresholdConstraintsListDataType{ - ThresholdConstraintsData: []model.ThresholdConstraintsDataType{ + sut := ThresholdConstraintsListDataType{ + ThresholdConstraintsData: []ThresholdConstraintsDataType{ { - ThresholdId: util.Ptr(model.ThresholdIdType(0)), - ThresholdRangeMin: model.NewScaledNumberType(1), + ThresholdId: util.Ptr(ThresholdIdType(0)), + ThresholdRangeMin: NewScaledNumberType(1), }, { - ThresholdId: util.Ptr(model.ThresholdIdType(1)), - ThresholdRangeMin: model.NewScaledNumberType(1), + ThresholdId: util.Ptr(ThresholdIdType(1)), + ThresholdRangeMin: NewScaledNumberType(1), }, }, } - newData := model.ThresholdConstraintsListDataType{ - ThresholdConstraintsData: []model.ThresholdConstraintsDataType{ + newData := ThresholdConstraintsListDataType{ + ThresholdConstraintsData: []ThresholdConstraintsDataType{ { - ThresholdId: util.Ptr(model.ThresholdIdType(1)), - ThresholdRangeMin: model.NewScaledNumberType(10), + ThresholdId: util.Ptr(ThresholdIdType(1)), + ThresholdRangeMin: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ThresholdConstraintsData // check the non changing items @@ -85,30 +84,30 @@ func TestThresholdConstraintsListDataType_Update(t *testing.T) { } func TestThresholdDescriptionListDataType_Update(t *testing.T) { - sut := model.ThresholdDescriptionListDataType{ - ThresholdDescriptionData: []model.ThresholdDescriptionDataType{ + sut := ThresholdDescriptionListDataType{ + ThresholdDescriptionData: []ThresholdDescriptionDataType{ { - ThresholdId: util.Ptr(model.ThresholdIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + ThresholdId: util.Ptr(ThresholdIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - ThresholdId: util.Ptr(model.ThresholdIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + ThresholdId: util.Ptr(ThresholdIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.ThresholdDescriptionListDataType{ - ThresholdDescriptionData: []model.ThresholdDescriptionDataType{ + newData := ThresholdDescriptionListDataType{ + ThresholdDescriptionData: []ThresholdDescriptionDataType{ { - ThresholdId: util.Ptr(model.ThresholdIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + ThresholdId: util.Ptr(ThresholdIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.ThresholdDescriptionData // check the non changing items diff --git a/spine/model/timeseries_additions_test.go b/spine/model/timeseries_additions_test.go index 21630197..654cd409 100644 --- a/spine/model/timeseries_additions_test.go +++ b/spine/model/timeseries_additions_test.go @@ -1,42 +1,41 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestTimeSeriesListDataType_Update(t *testing.T) { - sut := model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ + sut := TimeSeriesListDataType{ + TimeSeriesData: []TimeSeriesDataType{ { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesSlot: []model.TimeSeriesSlotType{ + TimeSeriesId: util.Ptr(TimeSeriesIdType(0)), + TimeSeriesSlot: []TimeSeriesSlotType{ { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(0)), }, }, }, { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - TimeSeriesSlot: []model.TimeSeriesSlotType{ + TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), + TimeSeriesSlot: []TimeSeriesSlotType{ { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(0)), }, }, }, }, } - newData := model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ + newData := TimeSeriesListDataType{ + TimeSeriesData: []TimeSeriesDataType{ { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - TimeSeriesSlot: []model.TimeSeriesSlotType{ + TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), + TimeSeriesSlot: []TimeSeriesSlotType{ { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(1)), }, }, }, @@ -44,7 +43,7 @@ func TestTimeSeriesListDataType_Update(t *testing.T) { } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TimeSeriesData // check the non changing items @@ -59,88 +58,88 @@ func TestTimeSeriesListDataType_Update(t *testing.T) { } func TestTimeSeriesListDataType_Update_02(t *testing.T) { - sut := model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ + sut := TimeSeriesListDataType{ + TimeSeriesData: []TimeSeriesDataType{ { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - TimePeriod: &model.TimePeriodType{ - StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), - EndTime: util.Ptr(model.AbsoluteOrRelativeTimeType("P6D")), + TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), + TimePeriod: &TimePeriodType{ + StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), + EndTime: util.Ptr(AbsoluteOrRelativeTimeType("P6D")), }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ + TimeSeriesSlot: []TimeSeriesSlotType{ { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - TimePeriod: &model.TimePeriodType{ - StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), - EndTime: util.Ptr(model.AbsoluteOrRelativeTimeType("P6D")), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(0)), + TimePeriod: &TimePeriodType{ + StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), + EndTime: util.Ptr(AbsoluteOrRelativeTimeType("P6D")), }, - MaxValue: model.NewScaledNumberType(10000), + MaxValue: NewScaledNumberType(10000), }, }, }, { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimePeriod: &model.TimePeriodType{ - StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), + TimeSeriesId: util.Ptr(TimeSeriesIdType(2)), + TimePeriod: &TimePeriodType{ + StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ + TimeSeriesSlot: []TimeSeriesSlotType{ { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Duration: util.Ptr(model.DurationType("P1DT6H46M33S")), - MaxValue: model.NewScaledNumberType(0), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(0)), + Duration: util.Ptr(DurationType("P1DT6H46M33S")), + MaxValue: NewScaledNumberType(0), }, { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("PT7H37M53S")), - MaxValue: model.NewScaledNumberType(4410), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(1)), + Duration: util.Ptr(DurationType("PT7H37M53S")), + MaxValue: NewScaledNumberType(4410), }, { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(2)), - Duration: util.Ptr(model.DurationType("PT38M")), - MaxValue: model.NewScaledNumberType(0), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(2)), + Duration: util.Ptr(DurationType("PT38M")), + MaxValue: NewScaledNumberType(0), }, { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(3)), - Duration: util.Ptr(model.DurationType("PT32M")), - MaxValue: model.NewScaledNumberType(4410), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(3)), + Duration: util.Ptr(DurationType("PT32M")), + MaxValue: NewScaledNumberType(4410), }, { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(4)), - Duration: util.Ptr(model.DurationType("P1D")), - MaxValue: model.NewScaledNumberType(0), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(4)), + Duration: util.Ptr(DurationType("P1D")), + MaxValue: NewScaledNumberType(0), }, }, }, { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), - TimePeriod: &model.TimePeriodType{ - StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), + TimeSeriesId: util.Ptr(TimeSeriesIdType(3)), + TimePeriod: &TimePeriodType{ + StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ + TimeSeriesSlot: []TimeSeriesSlotType{ { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("P1DT15H24M57S")), - Value: model.NewScaledNumberType(44229), - MaxValue: model.NewScaledNumberType(49629), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(1)), + Duration: util.Ptr(DurationType("P1DT15H24M57S")), + Value: NewScaledNumberType(44229), + MaxValue: NewScaledNumberType(49629), }, }, }, }, } - newData := model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ + newData := TimeSeriesListDataType{ + TimeSeriesData: []TimeSeriesDataType{ { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), - TimePeriod: &model.TimePeriodType{ - StartTime: util.Ptr(model.AbsoluteOrRelativeTimeType("PT0S")), + TimeSeriesId: util.Ptr(TimeSeriesIdType(3)), + TimePeriod: &TimePeriodType{ + StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ + TimeSeriesSlot: []TimeSeriesSlotType{ { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("P1DT15H16M50S")), - Value: model.NewScaledNumberType(11539), - MaxValue: model.NewScaledNumberType(49629), + TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(1)), + Duration: util.Ptr(DurationType("P1DT15H16M50S")), + Value: NewScaledNumberType(11539), + MaxValue: NewScaledNumberType(49629), }, }, }, @@ -148,7 +147,7 @@ func TestTimeSeriesListDataType_Update_02(t *testing.T) { } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TimeSeriesData // check the non changing items @@ -164,30 +163,30 @@ func TestTimeSeriesListDataType_Update_02(t *testing.T) { } func TestTimeSeriesDescriptionListDataType_Update(t *testing.T) { - sut := model.TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + sut := TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []TimeSeriesDescriptionDataType{ { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + TimeSeriesId: util.Ptr(TimeSeriesIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + newData := TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []TimeSeriesDescriptionDataType{ { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TimeSeriesDescriptionData // check the non changing items @@ -202,30 +201,30 @@ func TestTimeSeriesDescriptionListDataType_Update(t *testing.T) { } func TestTimeSeriesConstraintsListDataType_Update(t *testing.T) { - sut := model.TimeSeriesConstraintsListDataType{ - TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ + sut := TimeSeriesConstraintsListDataType{ + TimeSeriesConstraintsData: []TimeSeriesConstraintsDataType{ { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - SlotValueMin: model.NewScaledNumberType(1), + TimeSeriesId: util.Ptr(TimeSeriesIdType(0)), + SlotValueMin: NewScaledNumberType(1), }, { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - SlotValueMin: model.NewScaledNumberType(1), + TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), + SlotValueMin: NewScaledNumberType(1), }, }, } - newData := model.TimeSeriesConstraintsListDataType{ - TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ + newData := TimeSeriesConstraintsListDataType{ + TimeSeriesConstraintsData: []TimeSeriesConstraintsDataType{ { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - SlotValueMin: model.NewScaledNumberType(10), + TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), + SlotValueMin: NewScaledNumberType(10), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TimeSeriesConstraintsData // check the non changing items diff --git a/spine/model/timetable_additions_test.go b/spine/model/timetable_additions_test.go index 9f6afc42..0f4c9844 100644 --- a/spine/model/timetable_additions_test.go +++ b/spine/model/timetable_additions_test.go @@ -1,36 +1,35 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) func TestTimeTableListDataType_Update(t *testing.T) { - sut := model.TimeTableListDataType{ - TimeTableData: []model.TimeTableDataType{ + sut := TimeTableListDataType{ + TimeTableData: []TimeTableDataType{ { - TimeTableId: util.Ptr(model.TimeTableIdType(0)), - RecurrenceInformation: &model.RecurrenceInformationType{ + TimeTableId: util.Ptr(TimeTableIdType(0)), + RecurrenceInformation: &RecurrenceInformationType{ ExecutionCount: util.Ptr(uint(1)), }, }, { - TimeTableId: util.Ptr(model.TimeTableIdType(1)), - RecurrenceInformation: &model.RecurrenceInformationType{ + TimeTableId: util.Ptr(TimeTableIdType(1)), + RecurrenceInformation: &RecurrenceInformationType{ ExecutionCount: util.Ptr(uint(1)), }, }, }, } - newData := model.TimeTableListDataType{ - TimeTableData: []model.TimeTableDataType{ + newData := TimeTableListDataType{ + TimeTableData: []TimeTableDataType{ { - TimeTableId: util.Ptr(model.TimeTableIdType(1)), - RecurrenceInformation: &model.RecurrenceInformationType{ + TimeTableId: util.Ptr(TimeTableIdType(1)), + RecurrenceInformation: &RecurrenceInformationType{ ExecutionCount: util.Ptr(uint(10)), }, }, @@ -38,7 +37,7 @@ func TestTimeTableListDataType_Update(t *testing.T) { } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TimeTableData // check the non changing items @@ -53,30 +52,30 @@ func TestTimeTableListDataType_Update(t *testing.T) { } func TestTimeTableConstraintsListDataType_Update(t *testing.T) { - sut := model.TimeTableConstraintsListDataType{ - TimeTableConstraintsData: []model.TimeTableConstraintsDataType{ + sut := TimeTableConstraintsListDataType{ + TimeTableConstraintsData: []TimeTableConstraintsDataType{ { - TimeTableId: util.Ptr(model.TimeTableIdType(0)), - SlotCountMin: util.Ptr(model.TimeSlotCountType(1)), + TimeTableId: util.Ptr(TimeTableIdType(0)), + SlotCountMin: util.Ptr(TimeSlotCountType(1)), }, { - TimeTableId: util.Ptr(model.TimeTableIdType(1)), - SlotCountMin: util.Ptr(model.TimeSlotCountType(1)), + TimeTableId: util.Ptr(TimeTableIdType(1)), + SlotCountMin: util.Ptr(TimeSlotCountType(1)), }, }, } - newData := model.TimeTableConstraintsListDataType{ - TimeTableConstraintsData: []model.TimeTableConstraintsDataType{ + newData := TimeTableConstraintsListDataType{ + TimeTableConstraintsData: []TimeTableConstraintsDataType{ { - TimeTableId: util.Ptr(model.TimeTableIdType(1)), - SlotCountMin: util.Ptr(model.TimeSlotCountType(10)), + TimeTableId: util.Ptr(TimeTableIdType(1)), + SlotCountMin: util.Ptr(TimeSlotCountType(10)), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TimeTableConstraintsData // check the non changing items @@ -91,30 +90,30 @@ func TestTimeTableConstraintsListDataType_Update(t *testing.T) { } func TestTimeTableDescriptionListDataType_Update(t *testing.T) { - sut := model.TimeTableDescriptionListDataType{ - TimeTableDescriptionData: []model.TimeTableDescriptionDataType{ + sut := TimeTableDescriptionListDataType{ + TimeTableDescriptionData: []TimeTableDescriptionDataType{ { - TimeTableId: util.Ptr(model.TimeTableIdType(0)), - Description: util.Ptr(model.DescriptionType("old")), + TimeTableId: util.Ptr(TimeTableIdType(0)), + Description: util.Ptr(DescriptionType("old")), }, { - TimeTableId: util.Ptr(model.TimeTableIdType(1)), - Description: util.Ptr(model.DescriptionType("old")), + TimeTableId: util.Ptr(TimeTableIdType(1)), + Description: util.Ptr(DescriptionType("old")), }, }, } - newData := model.TimeTableDescriptionListDataType{ - TimeTableDescriptionData: []model.TimeTableDescriptionDataType{ + newData := TimeTableDescriptionListDataType{ + TimeTableDescriptionData: []TimeTableDescriptionDataType{ { - TimeTableId: util.Ptr(model.TimeTableIdType(1)), - Description: util.Ptr(model.DescriptionType("new")), + TimeTableId: util.Ptr(TimeTableIdType(1)), + Description: util.Ptr(DescriptionType("new")), }, }, } // Act - sut.UpdateList(&newData, model.NewFilterTypePartial(), nil) + sut.UpdateList(&newData, NewFilterTypePartial(), nil) data := sut.TimeTableDescriptionData // check the non changing items diff --git a/spine/model/update_test.go b/spine/model/update_test.go index 2ef5a267..166adfcf 100644 --- a/spine/model/update_test.go +++ b/spine/model/update_test.go @@ -1,9 +1,8 @@ -package model_test +package model import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) @@ -25,7 +24,7 @@ func TestUpdateList_NewItem(t *testing.T) { expectedResult := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(1))}, {Id: util.Ptr(uint(2)), DataItem: util.Ptr(int(2))}} // Act - result := model.UpdateList(existingData, newData, nil, nil) + result := UpdateList(existingData, newData, nil, nil) assert.Equal(t, expectedResult, result) } @@ -37,7 +36,7 @@ func TestUpdateList_ChangedItem(t *testing.T) { expectedResult := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(2))}} // Act - result := model.UpdateList(existingData, newData, nil, nil) + result := UpdateList(existingData, newData, nil, nil) assert.Equal(t, expectedResult, result) } @@ -49,7 +48,7 @@ func TestUpdateList_NewAndChangedItem(t *testing.T) { expectedResult := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(2))}, {Id: util.Ptr(uint(3)), DataItem: util.Ptr(int(3))}} // Act - result := model.UpdateList(existingData, newData, nil, nil) + result := UpdateList(existingData, newData, nil, nil) assert.Equal(t, expectedResult, result) } @@ -61,28 +60,28 @@ func TestUpdateList_ItemWithNoIdentifier(t *testing.T) { expectedResult := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(3))}, {Id: util.Ptr(uint(2)), DataItem: util.Ptr(int(3))}} // Act - result := model.UpdateList(existingData, newData, nil, nil) + result := UpdateList(existingData, newData, nil, nil) assert.Equal(t, expectedResult, result) } func TestRemoveFieldFromType(t *testing.T) { - items := &model.LoadControlLimitListDataType{ - LoadControlLimitData: []model.LoadControlLimitDataType{ + items := &LoadControlLimitListDataType{ + LoadControlLimitData: []LoadControlLimitDataType{ { - LimitId: util.Ptr(model.LoadControlLimitIdType(1)), - Value: model.NewScaledNumberType(16.0), + LimitId: util.Ptr(LoadControlLimitIdType(1)), + Value: NewScaledNumberType(16.0), }, }, } - elements := &model.LoadControlLimitDataElementsType{ - Value: &model.ScaledNumberElementsType{}, + elements := &LoadControlLimitDataElementsType{ + Value: &ScaledNumberElementsType{}, } - model.RemoveElementFromItem(&items.LoadControlLimitData[0], elements) + RemoveElementFromItem(&items.LoadControlLimitData[0], elements) - var nilValue *model.ScaledNumberType + var nilValue *ScaledNumberType assert.Equal(t, nilValue, items.LoadControlLimitData[0].Value) } @@ -99,7 +98,7 @@ func TestUpdateList_UpdateSelector(t *testing.T) { expectedResult := []TestUpdateData{{Id: util.Ptr(1), DataItem: 3}, {Id: util.Ptr(2), DataItem: 2}} // Act - result := model.UpdateList[TestUpdateData](existingData, newData, dataProvider) + result := UpdateList[TestUpdateData](existingData, newData, dataProvider) assert.Equal(t, expectedResult, result) } @@ -114,7 +113,7 @@ func TestUpdateList_DeleteSelector(t *testing.T) { expectedResult := []TestUpdateData{{Id: util.Ptr(2), DataItem: 2}} // Act - result := model.UpdateList[TestUpdateData](existingData, newData, dataProvider) + result := UpdateList[TestUpdateData](existingData, newData, dataProvider) assert.Equal(t, expectedResult, result) } From 3005b45429bcedaba127a5c1a286bdd4a4015c57 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:51:20 +0100 Subject: [PATCH 125/240] Make model update work with non struct types --- spine/model/update.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spine/model/update.go b/spine/model/update.go index 6eea837a..8feec337 100644 --- a/spine/model/update.go +++ b/spine/model/update.go @@ -52,6 +52,10 @@ func keyFieldNames(item any) []string { v := reflect.ValueOf(item) t := reflect.TypeOf(item) + if v.Kind() != reflect.Struct { + return result + } + for i := 0; i < v.NumField(); i++ { f := v.Field(i) if f.Kind() != reflect.Ptr { From 288bc632670a55efa9373c159dbfd8eab578ad5d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 20:51:32 +0100 Subject: [PATCH 126/240] Add version test --- spine/model/version_additions_test.go | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 spine/model/version_additions_test.go diff --git a/spine/model/version_additions_test.go b/spine/model/version_additions_test.go new file mode 100644 index 00000000..7cf49df7 --- /dev/null +++ b/spine/model/version_additions_test.go @@ -0,0 +1,48 @@ +package model + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestVersionSuite(t *testing.T) { + suite.Run(t, new(VersionSuite)) +} + +type VersionSuite struct { + suite.Suite +} + +func (s *VersionSuite) SetupSuite() {} +func (s *VersionSuite) TearDownTest() {} + +func (s *VersionSuite) BeforeTest(suiteName, testName string) {} + +func (s *VersionSuite) Test_UpdateList() { + sut := SpecificationVersionListDataType{ + SpecificationVersionData: []SpecificationVersionDataType{ + SpecificationVersionDataType("1.0.0"), + }, + } + + newData := SpecificationVersionListDataType{ + SpecificationVersionData: []SpecificationVersionDataType{ + SpecificationVersionDataType("1.0.1"), + }, + } + + data := sut.SpecificationVersionData + // check properties of updated item + item1 := data[0] + assert.Equal(s.T(), "1.0.0", string(item1)) + + // Act + sut.UpdateList(&newData, NewFilterTypePartial(), nil) + + data = sut.SpecificationVersionData + // check properties of updated item + item1 = data[0] + assert.Equal(s.T(), "1.0.1", string(item1)) +} From 59441413a2ebcfa42192c6eaad554345ec6d64f7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 21:25:40 +0100 Subject: [PATCH 127/240] Remove update support UseCaseInformationListData The documentation does not detail if this should be supported at all, and if so how. So remove it for now --- spine/model/usecaseinformation_additions.go | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 spine/model/usecaseinformation_additions.go diff --git a/spine/model/usecaseinformation_additions.go b/spine/model/usecaseinformation_additions.go deleted file mode 100644 index 45e70f65..00000000 --- a/spine/model/usecaseinformation_additions.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -// UseCaseInformationListDataType - -var _ Updater = (*UseCaseInformationListDataType)(nil) - -func (r *UseCaseInformationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []UseCaseInformationDataType - if newList != nil { - newData = newList.(*UseCaseInformationListDataType).UseCaseInformationData - } - - r.UseCaseInformationData = UpdateList(r.UseCaseInformationData, newData, filterPartial, filterDelete) -} From e07d003849fc6ca85361058c1906b704bbffe23b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 21:50:39 +0100 Subject: [PATCH 128/240] Add mandatory address to usecase info Add mandatory device address to useCaseInformation --- spine/device.go | 6 +++--- spine/usecase_manager.go | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/spine/device.go b/spine/device.go index 3d2b6c50..97ad813e 100644 --- a/spine/device.go +++ b/spine/device.go @@ -13,9 +13,9 @@ type DeviceImpl struct { // Both values are required for a local device but provided as empty strings for a remote device // as the address is only provided via detailed discovery response func NewDeviceImpl(address *model.AddressDeviceType, dType *model.DeviceTypeType, featureSet *model.NetworkManagementFeatureSetType) *DeviceImpl { - deviceImpl := &DeviceImpl{ - useCaseManager: NewUseCaseManager(), - } + deviceImpl := &DeviceImpl{} + + deviceImpl.useCaseManager = NewUseCaseManager(deviceImpl) if dType != nil { deviceImpl.dType = dType diff --git a/spine/usecase_manager.go b/spine/usecase_manager.go index ef460687..d5048f2f 100644 --- a/spine/usecase_manager.go +++ b/spine/usecase_manager.go @@ -1,17 +1,22 @@ package spine -import "github.com/enbility/eebus-go/spine/model" +import ( + "github.com/enbility/eebus-go/spine/model" +) // manages the supported usecases for a device // each device has its own UseCaseManager type UseCaseManager struct { useCaseInformationMap map[model.UseCaseActorType][]model.UseCaseSupportType + + localDevice *DeviceImpl } // return a new UseCaseManager -func NewUseCaseManager() *UseCaseManager { +func NewUseCaseManager(localDevice *DeviceImpl) *UseCaseManager { return &UseCaseManager{ useCaseInformationMap: make(map[model.UseCaseActorType][]model.UseCaseSupportType), + localDevice: localDevice, } } @@ -44,8 +49,14 @@ func (r *UseCaseManager) UseCaseInformation() []model.UseCaseInformationDataType for actor, useCaseSupport := range r.useCaseInformationMap { thisActor := actor + // according to ProtocolSpecification Version 1.3.0 chapter 7.5.2 + // the address is mandatory. At least the device address should be shown, + // preferably the entity and features as well + deviceAddress := &model.FeatureAddressType{ + Device: r.localDevice.Address(), + } useCaseInfo := model.UseCaseInformationDataType{ - //Address: r.address, // TODO: which address ??? + Address: deviceAddress, Actor: &thisActor, UseCaseSupport: useCaseSupport, } From fa83d10893c846c77a0be450c10a11227f9f36fb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Jan 2024 21:58:39 +0100 Subject: [PATCH 129/240] Add support for usecase subrevision --- spine/device_remote_test.go | 4 ++++ spine/nodemanagement_usecase.go | 6 ++++++ spine/usecase.go | 23 +++++++++++++++++++---- spine/usecase_manager.go | 13 ++++++++++++- spine/usecase_test.go | 1 + 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index d3256dc1..1e0b14ea 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -86,6 +86,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { model.UseCaseActorTypeBatterySystem, model.UseCaseNameTypeControlOfBattery, model.SpecificationVersionType("1.0.0"), + "", true, []model.UseCaseScenarioSupportType{1}, ) @@ -102,6 +103,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVCommissioningAndConfiguration, model.SpecificationVersionType("1.0.0"), + "", true, []model.UseCaseScenarioSupportType{1}, ) @@ -118,6 +120,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, model.SpecificationVersionType("1.0.0"), + "", false, []model.UseCaseScenarioSupportType{1}, ) @@ -134,6 +137,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, model.SpecificationVersionType("1.0.0"), + "", true, []model.UseCaseScenarioSupportType{1}, ) diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go index f2ba0c05..f21c6e63 100644 --- a/spine/nodemanagement_usecase.go +++ b/spine/nodemanagement_usecase.go @@ -84,6 +84,11 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data mode useCaseVersion = model.SpecificationVersionType(*useCaseSupport.UseCaseVersion) } + var useCaseDocumemtSubRevision string + if useCaseSupport.UseCaseDocumentSubRevision != nil { + useCaseDocumemtSubRevision = *useCaseSupport.UseCaseDocumentSubRevision + } + if useCaseSupport.ScenarioSupport == nil { logging.Log().Errorf("scenarioSupport is missing in useCaseSupport %s", useCaseName) continue @@ -93,6 +98,7 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data mode actor, useCaseName, useCaseVersion, + useCaseDocumemtSubRevision, useCaseAvailable, useCaseSupport.ScenarioSupport) } diff --git a/spine/usecase.go b/spine/usecase.go index 880873be..b28be496 100644 --- a/spine/usecase.go +++ b/spine/usecase.go @@ -79,20 +79,35 @@ type UseCaseImpl struct { } // returns a UseCaseImpl with a default mapping of entity to actor using data -func NewUseCase(entity *EntityLocalImpl, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { +func NewUseCase( + entity *EntityLocalImpl, + ucEnumType model.UseCaseNameType, + useCaseVersion model.SpecificationVersionType, + useCaseDocumemtSubRevision string, + useCaseAvailable bool, + scenarioSupport []model.UseCaseScenarioSupportType, +) *UseCaseImpl { checkEntityArguments(*entity.EntityImpl) actor := entityTypeActorMap[entity.EntityType()] - return NewUseCaseWithActor(entity, actor, ucEnumType, useCaseVersion, useCaseAvailable, scenarioSupport) + return NewUseCaseWithActor(entity, actor, ucEnumType, useCaseVersion, useCaseDocumemtSubRevision, useCaseAvailable, scenarioSupport) } // returns a UseCaseImpl with specific entity and actor -func NewUseCaseWithActor(entity *EntityLocalImpl, actor model.UseCaseActorType, ucEnumType model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarioSupport []model.UseCaseScenarioSupportType) *UseCaseImpl { +func NewUseCaseWithActor( + entity *EntityLocalImpl, + actor model.UseCaseActorType, + ucEnumType model.UseCaseNameType, + useCaseVersion model.SpecificationVersionType, + useCaseDocumemtSubRevision string, + useCaseAvailable bool, + scenarioSupport []model.UseCaseScenarioSupportType, +) *UseCaseImpl { checkUCArguments(actor, ucEnumType) ucManager := entity.Device().UseCaseManager() - ucManager.Add(actor, ucEnumType, useCaseVersion, useCaseAvailable, scenarioSupport) + ucManager.Add(actor, ucEnumType, useCaseVersion, useCaseDocumemtSubRevision, useCaseAvailable, scenarioSupport) return &UseCaseImpl{ Entity: entity, diff --git a/spine/usecase_manager.go b/spine/usecase_manager.go index d5048f2f..d4d24e29 100644 --- a/spine/usecase_manager.go +++ b/spine/usecase_manager.go @@ -21,7 +21,14 @@ func NewUseCaseManager(localDevice *DeviceImpl) *UseCaseManager { } // add a usecase -func (r *UseCaseManager) Add(actor model.UseCaseActorType, useCaseName model.UseCaseNameType, useCaseVersion model.SpecificationVersionType, useCaseAvailable bool, scenarios []model.UseCaseScenarioSupportType) { +func (r *UseCaseManager) Add( + actor model.UseCaseActorType, + useCaseName model.UseCaseNameType, + useCaseVersion model.SpecificationVersionType, + useCaseDocumemtSubRevision string, + useCaseAvailable bool, + scenarios []model.UseCaseScenarioSupportType, +) { useCaseSupport := model.UseCaseSupportType{ UseCaseVersion: &useCaseVersion, UseCaseName: &useCaseName, @@ -29,6 +36,10 @@ func (r *UseCaseManager) Add(actor model.UseCaseActorType, useCaseName model.Use ScenarioSupport: scenarios, } + if len(useCaseDocumemtSubRevision) > 0 { + useCaseSupport.UseCaseDocumentSubRevision = &useCaseDocumemtSubRevision + } + useCaseInfo, exists := r.useCaseInformationMap[actor] if !exists { useCaseInfo = make([]model.UseCaseSupportType, 0) diff --git a/spine/usecase_test.go b/spine/usecase_test.go index f2354435..415795e2 100644 --- a/spine/usecase_test.go +++ b/spine/usecase_test.go @@ -32,6 +32,7 @@ func (s *UsecaseSuite) Test_UseCase() { s.entity, model.UseCaseNameTypeControlOfBattery, model.SpecificationVersionType("1.0.0"), + "", true, []model.UseCaseScenarioSupportType{1}, ) From 5004e3c35382968656a6ecf9c05dd3ca96de996f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 6 Jan 2024 17:51:24 +0100 Subject: [PATCH 130/240] Add more locking to DeviceLocal --- spine/device_local.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spine/device_local.go b/spine/device_local.go index 404bfa4a..adc2951a 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -259,12 +259,18 @@ func (r *DeviceLocalImpl) HeartbeatManager() HeartbeatManager { } func (r *DeviceLocalImpl) AddEntity(entity *EntityLocalImpl) { + r.mux.Lock() + defer r.mux.Unlock() + r.entities = append(r.entities, entity) r.notifySubscribersOfEntity(entity, model.NetworkManagementStateChangeTypeAdded) } func (r *DeviceLocalImpl) RemoveEntity(entity *EntityLocalImpl) { + r.mux.Lock() + defer r.mux.Unlock() + for i, e := range r.entities { if e == entity { r.entities = append(r.entities[:i], r.entities[i+1:]...) @@ -275,10 +281,16 @@ func (r *DeviceLocalImpl) RemoveEntity(entity *EntityLocalImpl) { } func (r *DeviceLocalImpl) Entities() []*EntityLocalImpl { + r.mux.Lock() + defer r.mux.Unlock() + return r.entities } func (r *DeviceLocalImpl) Entity(id []model.AddressEntityType) *EntityLocalImpl { + r.mux.Lock() + defer r.mux.Unlock() + for _, e := range r.entities { if reflect.DeepEqual(id, e.Address().Entity) { return e @@ -288,6 +300,9 @@ func (r *DeviceLocalImpl) Entity(id []model.AddressEntityType) *EntityLocalImpl } func (r *DeviceLocalImpl) EntityForType(entityType model.EntityTypeType) *EntityLocalImpl { + r.mux.Lock() + defer r.mux.Unlock() + for _, e := range r.entities { if e.eType == entityType { return e @@ -319,6 +334,9 @@ func (r *DeviceLocalImpl) Information() *model.NodeManagementDetailedDiscoveryDe // send a notify message to all remote devices func (r *DeviceLocalImpl) NotifyUseCaseData() { + r.mux.Lock() + defer r.mux.Unlock() + for _, remoteDevice := range r.remoteDevices { // TODO: add error management _, _ = r.nodeManagement.NotifyUseCaseData(remoteDevice) From a696b29dd55d25b381e8710d5987a7be5dfd34b2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 6 Jan 2024 17:55:21 +0100 Subject: [PATCH 131/240] Update binding and subscriptions - Add Unsubscribe - Add Unbind - Update Sender - Remove remoteDevice from Subscribe and Bind calls - Add RemoveSubscription - Add RemoveAllSubscriptions - Add RemoveBinding - Add RemoveAllBindings --- features/feature.go | 4 +- spine/api.go | 12 +++- spine/device_local.go | 15 ++++- spine/entity_local.go | 12 ++++ spine/feature_local.go | 98 ++++++++++++++++++++++++++-- spine/mocks/Sender.go | 60 +++++++++++++++++ spine/nodemanagement_subscription.go | 2 +- spine/nodemanagement_test.go | 2 +- spine/send.go | 27 ++++++++ 9 files changed, 221 insertions(+), 11 deletions(-) diff --git a/features/feature.go b/features/feature.go index 20ea1c57..87902ab8 100644 --- a/features/feature.go +++ b/features/feature.go @@ -49,7 +49,7 @@ func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole mod // subscribe to the feature for a the entity func (f *FeatureImpl) SubscribeForEntity() error { - if _, fErr := f.featureLocal.Subscribe(f.featureRemote.Device(), f.featureRemote.Address()); fErr != nil { + if _, fErr := f.featureLocal.Subscribe(f.featureRemote.Address()); fErr != nil { return errors.New(fErr.String()) } @@ -62,7 +62,7 @@ func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType // bind to the feature of a the entity func (f *FeatureImpl) Bind() error { - if _, fErr := f.featureLocal.Bind(f.featureRemote.Device(), f.featureRemote.Address()); fErr != nil { + if _, fErr := f.featureLocal.Bind(f.featureRemote.Address()); fErr != nil { return errors.New(fErr.String()) } diff --git a/spine/api.go b/spine/api.go index 90060bb8..b016c2b7 100644 --- a/spine/api.go +++ b/spine/api.go @@ -53,10 +53,14 @@ type FeatureLocal interface { selector any, elements any, destination *FeatureRemoteImpl) (any, *model.ErrorType) - Subscribe(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) + Subscribe(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) // SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed - Bind(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) + RemoveSubscription(remoteAddress *model.FeatureAddressType) + RemoveAllSubscriptions() + Bind(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) // BindAndWait(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) *ErrorType + RemoveBinding(remoteAddress *model.FeatureAddressType) + RemoveAllBindings() NotifyData( function model.FunctionType, deleteSelector, partialSelector any, @@ -111,8 +115,12 @@ type Sender interface { Reply(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, cmd model.CmdType) error // Sends a call cmd with a subscription request Subscribe(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) + // Sends a call cmd with a subscription delete request + Unsubscribe(senderAddress, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) // Sends a call cmd with a binding request Bind(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) + // Sends a call cmd with a binding delte request + Unbind(senderAddress, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) // Sends a notify cmd to indicate that a subscribed feature changed Notify(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) // Sends a write cmd, setting properties of remote features diff --git a/spine/device_local.go b/spine/device_local.go index adc2951a..4e0aaa3c 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -124,7 +124,7 @@ func (r *DeviceLocalImpl) HandleEvent(payload EventPayload) { switch payload.Data.(type) { case *model.NodeManagementDetailedDiscoveryDataType: - _, _ = r.nodeManagement.Subscribe(payload.Feature.Device(), payload.Feature.Address()) + _, _ = r.nodeManagement.Subscribe(payload.Feature.Address()) // Request Use Case Data _, _ = r.nodeManagement.RequestUseCaseData(payload.Device.ski, payload.Device.Address(), payload.Device.Sender()) @@ -170,6 +170,19 @@ func (r *DeviceLocalImpl) RemoteDevices() []*DeviceRemoteImpl { return res } +func (r *DeviceLocalImpl) RemoteDeviceForAddress(address model.AddressDeviceType) *DeviceRemoteImpl { + r.mux.Lock() + defer r.mux.Unlock() + + for _, item := range r.remoteDevices { + if *item.address == address { + return item + } + } + + return nil +} + func (r *DeviceLocalImpl) RemoteDeviceForSki(ski string) *DeviceRemoteImpl { r.mux.Lock() defer r.mux.Unlock() diff --git a/spine/entity_local.go b/spine/entity_local.go index 2fe0561a..117c9909 100644 --- a/spine/entity_local.go +++ b/spine/entity_local.go @@ -86,4 +86,16 @@ func (r *EntityLocalImpl) Information() *model.NodeManagementDetailedDiscoveryEn } return &res +// Remove all subscriptions +func (r *EntityLocalImpl) RemoveAllSubscriptions() { + for _, item := range r.features { + item.RemoveAllSubscriptions() + } +} + +// Remove all bindings +func (r *EntityLocalImpl) RemoveAllBindings() { + for _, item := range r.features { + item.RemoveAllBindings() + } } diff --git a/spine/feature_local.go b/spine/feature_local.go index c5423588..e69ac750 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -2,6 +2,7 @@ package spine import ( "fmt" + "reflect" "sync" "time" @@ -22,6 +23,9 @@ type FeatureLocalImpl struct { resultHandler []FeatureResult resultCallback map[model.MsgCounterType]func(result ResultMessage) + bindings []*model.FeatureAddressType + subscriptions []*model.FeatureAddressType + mux sync.Mutex } @@ -185,19 +189,62 @@ func (r *FeatureLocalImpl) RequestAndFetchData( } // Subscribe to a remote feature -func (r *FeatureLocalImpl) Subscribe(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { +func (r *FeatureLocalImpl) Subscribe(remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { + remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) + if remoteDevice == nil { + return nil, model.NewErrorTypeFromString("device not found") + } + if r.Role() == model.RoleTypeServer { return nil, model.NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) } - msgCounter, err := remoteDevice.Sender().Subscribe(r.Address(), remoteAdress, r.ftype) + msgCounter, err := remoteDevice.Sender().Subscribe(r.Address(), remoteAddress, r.ftype) if err != nil { return nil, model.NewErrorTypeFromString(err.Error()) } + r.mux.Lock() + r.subscriptions = append(r.subscriptions, remoteAddress) + r.mux.Unlock() + return msgCounter, nil } +// Remove a subscriptions to a remote feature +func (r *FeatureLocalImpl) RemoveSubscription(remoteAddress *model.FeatureAddressType) { + remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) + if remoteDevice == nil { + return + } + + if _, err := remoteDevice.Sender().Unsubscribe(r.Address(), remoteAddress); err != nil { + return + } + + var subscriptions []*model.FeatureAddressType + + r.mux.Lock() + defer r.mux.Unlock() + + for _, item := range r.subscriptions { + if reflect.DeepEqual(item, remoteAddress) { + continue + } + + subscriptions = append(subscriptions, item) + } + + r.subscriptions = subscriptions +} + +// Remove all subscriptions to remote features +func (r *FeatureLocalImpl) RemoveAllSubscriptions() { + for _, item := range r.subscriptions { + r.RemoveSubscription(item) + } +} + /* TODO: check if this function is needed and can be fixed, see https://github.com/enbility/eebus-go/issues/31 // Subscribe to a remote feature and wait for the result @@ -227,9 +274,14 @@ func (r *FeatureLocalImpl) SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remo */ // Bind to a remote feature -func (r *FeatureLocalImpl) Bind(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { +func (r *FeatureLocalImpl) Bind(remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { + remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) + if remoteDevice == nil { + return nil, model.NewErrorTypeFromString("device not found") + } + if r.Role() == model.RoleTypeServer { - return nil, model.NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) + return nil, model.NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a binding", r)) } msgCounter, err := remoteDevice.Sender().Bind(r.Address(), remoteAddress, r.ftype) @@ -237,9 +289,47 @@ func (r *FeatureLocalImpl) Bind(remoteDevice *DeviceRemoteImpl, remoteAddress *m return nil, model.NewErrorTypeFromString(err.Error()) } + r.mux.Lock() + r.bindings = append(r.bindings, remoteAddress) + r.mux.Unlock() + return msgCounter, nil } +// Remove a binding to a remote feature +func (r *FeatureLocalImpl) RemoveBinding(remoteAddress *model.FeatureAddressType) { + remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) + if remoteDevice == nil { + return + } + + if _, err := remoteDevice.Sender().Unbind(r.Address(), remoteAddress); err != nil { + return + } + + var bindings []*model.FeatureAddressType + + r.mux.Lock() + defer r.mux.Unlock() + + for _, item := range r.bindings { + if reflect.DeepEqual(item, remoteAddress) { + continue + } + + bindings = append(bindings, item) + } + + r.bindings = bindings +} + +// Remove all subscriptions to remote features +func (r *FeatureLocalImpl) RemoveAllBindings() { + for _, item := range r.bindings { + r.RemoveBinding(item) + } +} + /* TODO: check if this function is needed and can be fixed, see https://github.com/enbility/eebus-go/issues/31 // Bind to a remote feature and wait for the result diff --git a/spine/mocks/Sender.go b/spine/mocks/Sender.go index a47f10e9..18498168 100644 --- a/spine/mocks/Sender.go +++ b/spine/mocks/Sender.go @@ -214,6 +214,66 @@ func (_m *Sender) Subscribe(senderAddress *model.FeatureAddressType, destination return r0, r1 } +// Unbind provides a mock function with given fields: senderAddress, destinationAddress +func (_m *Sender) Unbind(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) { + ret := _m.Called(senderAddress, destinationAddress) + + if len(ret) == 0 { + panic("no return value specified for Unbind") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType) (*model.MsgCounterType, error)); ok { + return rf(senderAddress, destinationAddress) + } + if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType) *model.MsgCounterType); ok { + r0 = rf(senderAddress, destinationAddress) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType) error); ok { + r1 = rf(senderAddress, destinationAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Unsubscribe provides a mock function with given fields: senderAddress, destinationAddress +func (_m *Sender) Unsubscribe(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) { + ret := _m.Called(senderAddress, destinationAddress) + + if len(ret) == 0 { + panic("no return value specified for Unsubscribe") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType) (*model.MsgCounterType, error)); ok { + return rf(senderAddress, destinationAddress) + } + if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType) *model.MsgCounterType); ok { + r0 = rf(senderAddress, destinationAddress) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType) error); ok { + r1 = rf(senderAddress, destinationAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Write provides a mock function with given fields: senderAddress, destinationAddress, cmd func (_m *Sender) Write(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) { ret := _m.Called(senderAddress, destinationAddress, cmd) diff --git a/spine/nodemanagement_subscription.go b/spine/nodemanagement_subscription.go index 181ef83e..a221fb24 100644 --- a/spine/nodemanagement_subscription.go +++ b/spine/nodemanagement_subscription.go @@ -18,7 +18,7 @@ func NewNodeManagementSubscriptionRequestCallType(clientAddress *model.FeatureAd } } -func NewNodeManagementSubscriptionDeleteCallType(clientAddress *model.FeatureAddressType, serverAddress *model.FeatureAddressType, featureType model.FeatureTypeType) *model.NodeManagementSubscriptionDeleteCallType { +func NewNodeManagementSubscriptionDeleteCallType(clientAddress *model.FeatureAddressType, serverAddress *model.FeatureAddressType) *model.NodeManagementSubscriptionDeleteCallType { return &model.NodeManagementSubscriptionDeleteCallType{ SubscriptionDelete: &model.SubscriptionManagementDeleteCallType{ ClientAddress: clientAddress, diff --git a/spine/nodemanagement_test.go b/spine/nodemanagement_test.go index dbe33736..dce5fd71 100644 --- a/spine/nodemanagement_test.go +++ b/spine/nodemanagement_test.go @@ -132,7 +132,7 @@ func TestNodemanagement_SubscriptionCalls(t *testing.T) { deleteMsg := Message{ Cmd: model.CmdType{ NodeManagementSubscriptionDeleteCall: NewNodeManagementSubscriptionDeleteCallType( - clientFeature.Address(), serverFeature.Address(), featureType), + clientFeature.Address(), serverFeature.Address()), }, CmdClassifier: model.CmdClassifierTypeCall, FeatureRemote: clientFeature, diff --git a/spine/send.go b/spine/send.go index 55f4010a..0b28d1a7 100644 --- a/spine/send.go +++ b/spine/send.go @@ -225,6 +225,20 @@ func (c *SenderImpl) Subscribe(senderAddress, destinationAddress *model.FeatureA return c.Request(model.CmdClassifierTypeCall, localAddress, remoteAddress, true, []model.CmdType{cmd}) } +// Send a subscription deletion request to a remote server feature +func (c *SenderImpl) Unsubscribe(senderAddress, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) { + + cmd := model.CmdType{ + NodeManagementSubscriptionDeleteCall: NewNodeManagementSubscriptionDeleteCallType(senderAddress, destinationAddress), + } + + // we always send it to the remote NodeManagment feature, which always is at entity:[0],feature:0 + localAddress := NodeManagementAddress(senderAddress.Device) + remoteAddress := NodeManagementAddress(destinationAddress.Device) + + return c.Request(model.CmdClassifierTypeCall, localAddress, remoteAddress, true, []model.CmdType{cmd}) +} + // Send a binding request to a remote server feature func (c *SenderImpl) Bind(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) { cmd := model.CmdType{ @@ -238,6 +252,19 @@ func (c *SenderImpl) Bind(senderAddress, destinationAddress *model.FeatureAddres return c.Request(model.CmdClassifierTypeCall, localAddress, remoteAddress, true, []model.CmdType{cmd}) } +// Send a binding request to a remote server feature +func (c *SenderImpl) Unbind(senderAddress, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) { + cmd := model.CmdType{ + NodeManagementBindingDeleteCall: NewNodeManagementBindingDeleteCallType(senderAddress, destinationAddress), + } + + // we always send it to the remote NodeManagment feature, which always is at entity:[0],feature:0 + localAddress := NodeManagementAddress(senderAddress.Device) + remoteAddress := NodeManagementAddress(destinationAddress.Device) + + return c.Request(model.CmdClassifierTypeCall, localAddress, remoteAddress, true, []model.CmdType{cmd}) +} + func (c *SenderImpl) getMsgCounter() *model.MsgCounterType { // TODO: persistence i := model.MsgCounterType(atomic.AddUint64(&c.msgNum, 1)) From 2e4a51da480aba0890dd2ff366ec4f06dc0a6210 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 6 Jan 2024 17:55:58 +0100 Subject: [PATCH 132/240] Update entity removal --- spine/device_local.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spine/device_local.go b/spine/device_local.go index 4e0aaa3c..df5647f6 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -281,16 +281,20 @@ func (r *DeviceLocalImpl) AddEntity(entity *EntityLocalImpl) { } func (r *DeviceLocalImpl) RemoveEntity(entity *EntityLocalImpl) { + entity.RemoveAllSubscriptions() + entity.RemoveAllBindings() + r.mux.Lock() defer r.mux.Unlock() - for i, e := range r.entities { - if e == entity { - r.entities = append(r.entities[:i], r.entities[i+1:]...) - // TODO: delete subscriptions of removed entity (incl. delete call) - break + var entities []*EntityLocalImpl + for _, e := range r.entities { + if e != entity { + entities = append(entities, e) } } + + r.entities = entities } func (r *DeviceLocalImpl) Entities() []*EntityLocalImpl { From 55b73fd92a559abeb640306aa16cd9bbcb5988aa Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 12:02:24 +0100 Subject: [PATCH 133/240] Move gomock to supported fork --- go.mod | 2 +- go.sum | 29 ++++------------------------- service/hub_test.go | 2 +- service/mdns_test.go | 2 +- service/mock_hub_test.go | 31 ++++++++++++++++++------------- service/mock_mdns_test.go | 13 +++++++++---- service/mock_service_test.go | 19 ++++++++++++------- service/service_test.go | 2 +- ship/connection_test.go | 2 +- ship/mock_types_test.go | 2 +- ship/websocket_test.go | 2 +- 11 files changed, 50 insertions(+), 56 deletions(-) diff --git a/go.mod b/go.mod index 389c5597..70907b48 100644 --- a/go.mod +++ b/go.mod @@ -20,13 +20,13 @@ require ( github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df github.com/ahmetb/go-linq/v3 v3.2.0 github.com/godbus/dbus/v5 v5.1.0 - github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.1 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed github.com/rickb777/date v1.20.5 github.com/stretchr/testify v1.8.4 gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a + go.uber.org/mock v0.4.0 ) retract ( diff --git a/go.sum b/go.sum index 049ccd1f..5f35559f 100644 --- a/go.sum +++ b/go.sum @@ -7,19 +7,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed h1:AMm8KKtfeEhUlj45DYJBSMW2VcLO1Tss3jaMUqb+VvE= github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= -github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= @@ -40,53 +35,42 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -101,21 +85,16 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/service/hub_test.go b/service/hub_test.go index e516ad58..250ea6a3 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -14,11 +14,11 @@ import ( "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/ship/mocks" "github.com/enbility/eebus-go/spine/model" - gomock "github.com/golang/mock/gomock" "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" + "go.uber.org/mock/gomock" ) func TestHubSuite(t *testing.T) { diff --git a/service/mdns_test.go b/service/mdns_test.go index 807d9c31..bee6dc0e 100644 --- a/service/mdns_test.go +++ b/service/mdns_test.go @@ -8,10 +8,10 @@ import ( mdnsmocks "github.com/enbility/eebus-go/service/mdns/mocks" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" - gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" + gomock "go.uber.org/mock/gomock" ) func TestMdnsSuite(t *testing.T) { diff --git a/service/mock_hub_test.go b/service/mock_hub_test.go index fddaaff5..e8c68eb3 100644 --- a/service/mock_hub_test.go +++ b/service/mock_hub_test.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/enbility/eebus-go/service (interfaces: ServiceProvider,ConnectionsHub) +// +// Generated by this command: +// +// mockgen -destination=mock_hub_test.go -package=service github.com/enbility/eebus-go/service ServiceProvider,ConnectionsHub +// // Package service is a generated GoMock package. package service @@ -7,7 +12,7 @@ package service import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockServiceProvider is a mock of ServiceProvider interface. @@ -42,7 +47,7 @@ func (m *MockServiceProvider) AllowWaitingForTrust(arg0 string) bool { } // AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. -func (mr *MockServiceProviderMockRecorder) AllowWaitingForTrust(arg0 interface{}) *gomock.Call { +func (mr *MockServiceProviderMockRecorder) AllowWaitingForTrust(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockServiceProvider)(nil).AllowWaitingForTrust), arg0) } @@ -54,7 +59,7 @@ func (m *MockServiceProvider) RemoteSKIConnected(arg0 string) { } // RemoteSKIConnected indicates an expected call of RemoteSKIConnected. -func (mr *MockServiceProviderMockRecorder) RemoteSKIConnected(arg0 interface{}) *gomock.Call { +func (mr *MockServiceProviderMockRecorder) RemoteSKIConnected(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIConnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIConnected), arg0) } @@ -66,7 +71,7 @@ func (m *MockServiceProvider) RemoteSKIDisconnected(arg0 string) { } // RemoteSKIDisconnected indicates an expected call of RemoteSKIDisconnected. -func (mr *MockServiceProviderMockRecorder) RemoteSKIDisconnected(arg0 interface{}) *gomock.Call { +func (mr *MockServiceProviderMockRecorder) RemoteSKIDisconnected(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIDisconnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIDisconnected), arg0) } @@ -78,7 +83,7 @@ func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 *Conn } // ServicePairingDetailUpdate indicates an expected call of ServicePairingDetailUpdate. -func (mr *MockServiceProviderMockRecorder) ServicePairingDetailUpdate(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockServiceProviderMockRecorder) ServicePairingDetailUpdate(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServicePairingDetailUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServicePairingDetailUpdate), arg0, arg1) } @@ -90,7 +95,7 @@ func (m *MockServiceProvider) ServiceShipIDUpdate(arg0, arg1 string) { } // ServiceShipIDUpdate indicates an expected call of ServiceShipIDUpdate. -func (mr *MockServiceProviderMockRecorder) ServiceShipIDUpdate(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockServiceProviderMockRecorder) ServiceShipIDUpdate(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceShipIDUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServiceShipIDUpdate), arg0, arg1) } @@ -102,7 +107,7 @@ func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []*MdnsEntry) { } // VisibleMDNSRecordsUpdated indicates an expected call of VisibleMDNSRecordsUpdated. -func (mr *MockServiceProviderMockRecorder) VisibleMDNSRecordsUpdated(arg0 interface{}) *gomock.Call { +func (mr *MockServiceProviderMockRecorder) VisibleMDNSRecordsUpdated(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleMDNSRecordsUpdated", reflect.TypeOf((*MockServiceProvider)(nil).VisibleMDNSRecordsUpdated), arg0) } @@ -137,7 +142,7 @@ func (m *MockConnectionsHub) CancelPairingWithSKI(arg0 string) { } // CancelPairingWithSKI indicates an expected call of CancelPairingWithSKI. -func (mr *MockConnectionsHubMockRecorder) CancelPairingWithSKI(arg0 interface{}) *gomock.Call { +func (mr *MockConnectionsHubMockRecorder) CancelPairingWithSKI(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelPairingWithSKI", reflect.TypeOf((*MockConnectionsHub)(nil).CancelPairingWithSKI), arg0) } @@ -149,7 +154,7 @@ func (m *MockConnectionsHub) DisconnectSKI(arg0, arg1 string) { } // DisconnectSKI indicates an expected call of DisconnectSKI. -func (mr *MockConnectionsHubMockRecorder) DisconnectSKI(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockConnectionsHubMockRecorder) DisconnectSKI(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisconnectSKI", reflect.TypeOf((*MockConnectionsHub)(nil).DisconnectSKI), arg0, arg1) } @@ -161,7 +166,7 @@ func (m *MockConnectionsHub) InitiatePairingWithSKI(arg0 string) { } // InitiatePairingWithSKI indicates an expected call of InitiatePairingWithSKI. -func (mr *MockConnectionsHubMockRecorder) InitiatePairingWithSKI(arg0 interface{}) *gomock.Call { +func (mr *MockConnectionsHubMockRecorder) InitiatePairingWithSKI(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitiatePairingWithSKI", reflect.TypeOf((*MockConnectionsHub)(nil).InitiatePairingWithSKI), arg0) } @@ -175,7 +180,7 @@ func (m *MockConnectionsHub) PairingDetailForSki(arg0 string) *ConnectionStateDe } // PairingDetailForSki indicates an expected call of PairingDetailForSki. -func (mr *MockConnectionsHubMockRecorder) PairingDetailForSki(arg0 interface{}) *gomock.Call { +func (mr *MockConnectionsHubMockRecorder) PairingDetailForSki(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PairingDetailForSki", reflect.TypeOf((*MockConnectionsHub)(nil).PairingDetailForSki), arg0) } @@ -187,7 +192,7 @@ func (m *MockConnectionsHub) RegisterRemoteSKI(arg0 string, arg1 bool) { } // RegisterRemoteSKI indicates an expected call of RegisterRemoteSKI. -func (mr *MockConnectionsHubMockRecorder) RegisterRemoteSKI(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockConnectionsHubMockRecorder) RegisterRemoteSKI(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRemoteSKI", reflect.TypeOf((*MockConnectionsHub)(nil).RegisterRemoteSKI), arg0, arg1) } @@ -201,7 +206,7 @@ func (m *MockConnectionsHub) ServiceForSKI(arg0 string) *ServiceDetails { } // ServiceForSKI indicates an expected call of ServiceForSKI. -func (mr *MockConnectionsHubMockRecorder) ServiceForSKI(arg0 interface{}) *gomock.Call { +func (mr *MockConnectionsHubMockRecorder) ServiceForSKI(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceForSKI", reflect.TypeOf((*MockConnectionsHub)(nil).ServiceForSKI), arg0) } diff --git a/service/mock_mdns_test.go b/service/mock_mdns_test.go index 2207663f..bf8ff6f6 100644 --- a/service/mock_mdns_test.go +++ b/service/mock_mdns_test.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/enbility/eebus-go/service (interfaces: MdnsSearch,MdnsService) +// +// Generated by this command: +// +// mockgen -destination=mock_mdns_test.go -package=service github.com/enbility/eebus-go/service MdnsSearch,MdnsService +// // Package service is a generated GoMock package. package service @@ -7,7 +12,7 @@ package service import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockMdnsSearch is a mock of MdnsSearch interface. @@ -40,7 +45,7 @@ func (m *MockMdnsSearch) ReportMdnsEntries(arg0 map[string]*MdnsEntry) { } // ReportMdnsEntries indicates an expected call of ReportMdnsEntries. -func (mr *MockMdnsSearchMockRecorder) ReportMdnsEntries(arg0 interface{}) *gomock.Call { +func (mr *MockMdnsSearchMockRecorder) ReportMdnsEntries(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportMdnsEntries", reflect.TypeOf((*MockMdnsSearch)(nil).ReportMdnsEntries), arg0) } @@ -89,7 +94,7 @@ func (m *MockMdnsService) RegisterMdnsSearch(arg0 MdnsSearch) { } // RegisterMdnsSearch indicates an expected call of RegisterMdnsSearch. -func (mr *MockMdnsServiceMockRecorder) RegisterMdnsSearch(arg0 interface{}) *gomock.Call { +func (mr *MockMdnsServiceMockRecorder) RegisterMdnsSearch(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).RegisterMdnsSearch), arg0) } @@ -139,7 +144,7 @@ func (m *MockMdnsService) UnregisterMdnsSearch(arg0 MdnsSearch) { } // UnregisterMdnsSearch indicates an expected call of UnregisterMdnsSearch. -func (mr *MockMdnsServiceMockRecorder) UnregisterMdnsSearch(arg0 interface{}) *gomock.Call { +func (mr *MockMdnsServiceMockRecorder) UnregisterMdnsSearch(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).UnregisterMdnsSearch), arg0) } diff --git a/service/mock_service_test.go b/service/mock_service_test.go index 9741f384..19ab3765 100644 --- a/service/mock_service_test.go +++ b/service/mock_service_test.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/enbility/eebus-go/service (interfaces: EEBUSServiceHandler) +// +// Generated by this command: +// +// mockgen -destination=mock_service_test.go -package=service github.com/enbility/eebus-go/service EEBUSServiceHandler +// // Package service is a generated GoMock package. package service @@ -7,7 +12,7 @@ package service import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockEEBUSServiceHandler is a mock of EEBUSServiceHandler interface. @@ -42,7 +47,7 @@ func (m *MockEEBUSServiceHandler) AllowWaitingForTrust(arg0 string) bool { } // AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. -func (mr *MockEEBUSServiceHandlerMockRecorder) AllowWaitingForTrust(arg0 interface{}) *gomock.Call { +func (mr *MockEEBUSServiceHandlerMockRecorder) AllowWaitingForTrust(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).AllowWaitingForTrust), arg0) } @@ -54,7 +59,7 @@ func (m *MockEEBUSServiceHandler) RemoteSKIConnected(arg0 *EEBUSService, arg1 st } // RemoteSKIConnected indicates an expected call of RemoteSKIConnected. -func (mr *MockEEBUSServiceHandlerMockRecorder) RemoteSKIConnected(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockEEBUSServiceHandlerMockRecorder) RemoteSKIConnected(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIConnected", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).RemoteSKIConnected), arg0, arg1) } @@ -66,7 +71,7 @@ func (m *MockEEBUSServiceHandler) RemoteSKIDisconnected(arg0 *EEBUSService, arg1 } // RemoteSKIDisconnected indicates an expected call of RemoteSKIDisconnected. -func (mr *MockEEBUSServiceHandlerMockRecorder) RemoteSKIDisconnected(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockEEBUSServiceHandlerMockRecorder) RemoteSKIDisconnected(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIDisconnected", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).RemoteSKIDisconnected), arg0, arg1) } @@ -78,7 +83,7 @@ func (m *MockEEBUSServiceHandler) ServicePairingDetailUpdate(arg0 string, arg1 * } // ServicePairingDetailUpdate indicates an expected call of ServicePairingDetailUpdate. -func (mr *MockEEBUSServiceHandlerMockRecorder) ServicePairingDetailUpdate(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockEEBUSServiceHandlerMockRecorder) ServicePairingDetailUpdate(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServicePairingDetailUpdate", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).ServicePairingDetailUpdate), arg0, arg1) } @@ -90,7 +95,7 @@ func (m *MockEEBUSServiceHandler) ServiceShipIDUpdate(arg0, arg1 string) { } // ServiceShipIDUpdate indicates an expected call of ServiceShipIDUpdate. -func (mr *MockEEBUSServiceHandlerMockRecorder) ServiceShipIDUpdate(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockEEBUSServiceHandlerMockRecorder) ServiceShipIDUpdate(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceShipIDUpdate", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).ServiceShipIDUpdate), arg0, arg1) } @@ -102,7 +107,7 @@ func (m *MockEEBUSServiceHandler) VisibleRemoteServicesUpdated(arg0 *EEBUSServic } // VisibleRemoteServicesUpdated indicates an expected call of VisibleRemoteServicesUpdated. -func (mr *MockEEBUSServiceHandlerMockRecorder) VisibleRemoteServicesUpdated(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockEEBUSServiceHandlerMockRecorder) VisibleRemoteServicesUpdated(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleRemoteServicesUpdated", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).VisibleRemoteServicesUpdated), arg0, arg1) } diff --git a/service/service_test.go b/service/service_test.go index 5ff820dd..b14fc5b7 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -8,9 +8,9 @@ import ( "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/logging/mocks" "github.com/enbility/eebus-go/spine/model" - gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + gomock "go.uber.org/mock/gomock" ) func TestServiceSuite(t *testing.T) { diff --git a/ship/connection_test.go b/ship/connection_test.go index 9218d26f..dbb7227a 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -9,10 +9,10 @@ import ( "github.com/enbility/eebus-go/spine" spineMocks "github.com/enbility/eebus-go/spine/mocks" spineModel "github.com/enbility/eebus-go/spine/model" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" + "go.uber.org/mock/gomock" ) func TestConnectionSuite(t *testing.T) { diff --git a/ship/mock_types_test.go b/ship/mock_types_test.go index c1badea2..b19b32da 100644 --- a/ship/mock_types_test.go +++ b/ship/mock_types_test.go @@ -7,7 +7,7 @@ package ship import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockShipDataConnection is a mock of ShipDataConnection interface. diff --git a/ship/websocket_test.go b/ship/websocket_test.go index 78afea90..71a6051f 100644 --- a/ship/websocket_test.go +++ b/ship/websocket_test.go @@ -10,10 +10,10 @@ import ( "time" util "github.com/enbility/eebus-go/util" - "github.com/golang/mock/gomock" "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + "go.uber.org/mock/gomock" ) func TestWebsocketSuite(t *testing.T) { From 87807c962043b34181191ba83481f3c8b7ffb41b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 12:05:20 +0100 Subject: [PATCH 134/240] Update features and data handling - Make clear the FeatureLocal and FunctionData return data copies - Add SetData to FeatureRemoteImpl to make testing easier --- spine/api.go | 4 ++-- spine/feature_local.go | 4 ++-- spine/feature_remote.go | 11 ++++++++++- spine/function_data.go | 6 +++--- spine/function_data_test.go | 6 +++--- spine/heartbeat_manager_test.go | 4 ++-- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/spine/api.go b/spine/api.go index b016c2b7..e5d92789 100644 --- a/spine/api.go +++ b/spine/api.go @@ -28,7 +28,7 @@ type Feature interface { type FeatureLocal interface { Feature - Data(function model.FunctionType) any + DataCopy(function model.FunctionType) any SetData(function model.FunctionType, data any) AddResultHandler(handler FeatureResult) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg ResultMessage)) @@ -91,7 +91,7 @@ type FunctionDataCmd interface { type FunctionData interface { Function() model.FunctionType - DataAny() any + DataCopyAny() any UpdateDataAny(data any, filterPartial *model.FilterType, filterDelete *model.FilterType) } diff --git a/spine/feature_local.go b/spine/feature_local.go index e69ac750..e9447f76 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -68,11 +68,11 @@ func (r *FeatureLocalImpl) AddFunctionType(function model.FunctionType, read, wr r.operations[function] = NewOperations(read, write) } -func (r *FeatureLocalImpl) Data(function model.FunctionType) any { +func (r *FeatureLocalImpl) DataCopy(function model.FunctionType) any { r.mux.Lock() defer r.mux.Unlock() - return r.functionData(function).DataAny() + return r.functionData(function).DataCopyAny() } func (r *FeatureLocalImpl) SetData(function model.FunctionType, data any) { diff --git a/spine/feature_remote.go b/spine/feature_remote.go index 7fb8f0ab..46448a2c 100644 --- a/spine/feature_remote.go +++ b/spine/feature_remote.go @@ -44,7 +44,16 @@ func (r *FeatureRemoteImpl) Data(function model.FunctionType) any { r.mux.Lock() defer r.mux.Unlock() - return r.functionData(function).DataAny() + return r.functionData(function).DataCopyAny() +} + +func (r *FeatureRemoteImpl) SetData(function model.FunctionType, data any) { + r.mux.Lock() + + fd := r.functionData(function) + fd.UpdateDataAny(data, nil, nil) + + r.mux.Unlock() } func (r *FeatureRemoteImpl) UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType) { diff --git a/spine/function_data.go b/spine/function_data.go index d79f3891..1e120920 100644 --- a/spine/function_data.go +++ b/spine/function_data.go @@ -28,7 +28,7 @@ func (r *FunctionDataImpl[T]) Function() model.FunctionType { return r.functionType } -func (r *FunctionDataImpl[T]) Data() *T { +func (r *FunctionDataImpl[T]) DataCopy() *T { r.mux.Lock() defer r.mux.Unlock() @@ -70,8 +70,8 @@ func (r *FunctionDataImpl[T]) UpdateData(newData *T, filterPartial *model.Filter return nil } -func (r *FunctionDataImpl[T]) DataAny() any { - return r.Data() +func (r *FunctionDataImpl[T]) DataCopyAny() any { + return r.DataCopy() } func (r *FunctionDataImpl[T]) UpdateDataAny(newData any, filterPartial *model.FilterType, filterDelete *model.FilterType) { diff --git a/spine/function_data_test.go b/spine/function_data_test.go index 79027212..c2475484 100644 --- a/spine/function_data_test.go +++ b/spine/function_data_test.go @@ -15,7 +15,7 @@ func TestFunctionData_UpdateData(t *testing.T) { functionType := model.FunctionTypeDeviceClassificationManufacturerData sut := NewFunctionData[model.DeviceClassificationManufacturerDataType](functionType) sut.UpdateData(newData, nil, nil) - getData := sut.Data() + getData := sut.DataCopy() assert.Equal(t, newData.DeviceName, getData.DeviceName) assert.Equal(t, functionType, sut.Function()) @@ -25,7 +25,7 @@ func TestFunctionData_UpdateData(t *testing.T) { DeviceName: util.Ptr(model.DeviceClassificationStringType("new device name")), } sut.UpdateData(newData, nil, nil) - getNewData := sut.Data() + getNewData := sut.DataCopy() assert.Equal(t, newData.DeviceName, getNewData.DeviceName) assert.NotEqual(t, getData.DeviceName, getNewData.DeviceName) @@ -58,7 +58,7 @@ func TestFunctionData_UpdateDataPartial(t *testing.T) { err := sut.UpdateData(newData, &model.FilterType{CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}}, nil) if assert.Nil(t, err) { - getData := sut.Data() + getData := sut.DataCopy() assert.Equal(t, 1, len(getData.ElectricalConnectionPermittedValueSetData)) } } diff --git a/spine/heartbeat_manager_test.go b/spine/heartbeat_manager_test.go index 05c58644..dbcdf392 100644 --- a/spine/heartbeat_manager_test.go +++ b/spine/heartbeat_manager_test.go @@ -89,7 +89,7 @@ func (suite *HeartBeatManagerSuite) Test_HeartbeatSuccess() { err := suite.localDevice.ProcessCmd(datagram, suite.remoteDevice) assert.Nil(suite.T(), err) - data := localFeature.Data(model.FunctionTypeDeviceDiagnosisHeartbeatData) + data := localFeature.DataCopy(model.FunctionTypeDeviceDiagnosisHeartbeatData) assert.Nil(suite.T(), data) running := suite.sut.IsHeartbeatRunning() @@ -103,7 +103,7 @@ func (suite *HeartBeatManagerSuite) Test_HeartbeatSuccess() { running = suite.sut.IsHeartbeatRunning() assert.Equal(suite.T(), true, running) - data = localFeature.Data(model.FunctionTypeDeviceDiagnosisHeartbeatData) + data = localFeature.DataCopy(model.FunctionTypeDeviceDiagnosisHeartbeatData) assert.NotNil(suite.T(), data) fctData := data.(*model.DeviceDiagnosisHeartbeatDataType) From f9b4fa67ea0b0e4996bfc76c58642acb62dc953e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 12:06:12 +0100 Subject: [PATCH 135/240] Rename custom_error to custom model --- spine/model/{custom_error.go => custom.go} | 0 spine/model/{custom_error_test.go => custom_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename spine/model/{custom_error.go => custom.go} (100%) rename spine/model/{custom_error_test.go => custom_test.go} (100%) diff --git a/spine/model/custom_error.go b/spine/model/custom.go similarity index 100% rename from spine/model/custom_error.go rename to spine/model/custom.go diff --git a/spine/model/custom_error_test.go b/spine/model/custom_test.go similarity index 100% rename from spine/model/custom_error_test.go rename to spine/model/custom_test.go From a063168e0efee534fcda3a8dbd8ca997cc9b11f4 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 12:08:04 +0100 Subject: [PATCH 136/240] Update HeartbeatManager Instead of checking for the proper feature entity and feature, use a setter --- spine/api.go | 1 + spine/entity_local.go | 6 ++++++ spine/heartbeat_manager.go | 27 ++++++--------------------- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/spine/api.go b/spine/api.go index e5d92789..e6524536 100644 --- a/spine/api.go +++ b/spine/api.go @@ -167,6 +167,7 @@ type SubscriptionManager interface { type HeartbeatManager interface { IsHeartbeatRunning() bool UpdateHeartbeatOnSubscriptions() + SetLocalFeature(entity *EntityLocalImpl, feature FeatureLocal) StartHeartbeat() error StopHeartbeat() } diff --git a/spine/entity_local.go b/spine/entity_local.go index 117c9909..1b41ec5e 100644 --- a/spine/entity_local.go +++ b/spine/entity_local.go @@ -49,6 +49,12 @@ func (r *EntityLocalImpl) GetOrAddFeature(featureType model.FeatureTypeType, rol } f.SetDescriptionString(description) r.features = append(r.features, f) + + if role == model.RoleTypeServer && featureType == model.FeatureTypeTypeDeviceDiagnosis { + // Update HeartbeatManagerImpl + r.device.HeartbeatManager().SetLocalFeature(r, f) + } + return f } diff --git a/spine/heartbeat_manager.go b/spine/heartbeat_manager.go index ed74719f..3c6212cc 100644 --- a/spine/heartbeat_manager.go +++ b/spine/heartbeat_manager.go @@ -47,7 +47,7 @@ func (c *HeartbeatManagerImpl) IsHeartbeatRunning() bool { // check if there are any heartbeat subscriptions left, otherwise stop creating new ones // or start creating heartbeats again if needed func (c *HeartbeatManagerImpl) UpdateHeartbeatOnSubscriptions() { - if err := c.updateLocal(); err != nil { + if c.localEntity == nil { return } @@ -66,24 +66,9 @@ func (c *HeartbeatManagerImpl) UpdateHeartbeatOnSubscriptions() { } } -func (c *HeartbeatManagerImpl) updateLocal() error { - if c.localFeature == nil { - // search through all entities on the device - entities := c.localDevice.Entities() - for _, entity := range entities { - localFeature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - if localFeature != nil { - c.localFeature = localFeature - c.localEntity = entity - } - } - - if c.localFeature == nil { - return errors.New("Local feature for heartbeat sender address could not be found") - } - } - - return nil +func (c *HeartbeatManagerImpl) SetLocalFeature(entity *EntityLocalImpl, feature FeatureLocal) { + c.localEntity = entity + c.localFeature = feature } // Start setting heartbeat data @@ -91,8 +76,8 @@ func (c *HeartbeatManagerImpl) updateLocal() error { // otherwise this will end with an error // Note: Remote features need to have a subscription to get notifications func (c *HeartbeatManagerImpl) StartHeartbeat() error { - if err := c.updateLocal(); err != nil { - return err + if c.localEntity == nil { + return errors.New("unknown entity") } timeout, err := c.heartBeatTimeout.GetTimeDuration() From 47a958b76e435dd26985c97d0db405dbdde9d096 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 12:12:11 +0100 Subject: [PATCH 137/240] Refactor usecase handling - Remove UseCaseManager implementation - Instead usecases are now set on the entity - Use data factory instead of additional data storage only for use cases - Remove all supported usecases if an entity is removed - Add and update tests - DataFactory now also support NodeManagement features --- spine/device.go | 13 +- spine/device_local.go | 1 + spine/device_remote.go | 15 +- spine/device_remote_test.go | 92 +++++++--- spine/entity_local.go | 57 ++++++- spine/feature_local.go | 3 + spine/function_data_factory.go | 14 +- spine/function_data_factory_test.go | 2 +- spine/model/nodemanagement_additions.go | 129 ++++++++++++++ spine/model/usecaseinformation_additions.go | 54 ++++++ spine/nodemanagement_usecase.go | 81 +-------- ...usecaseinformationlistdata_recv_reply.json | 120 +++++++++++++ spine/usecase.go | 157 ------------------ spine/usecase_manager.go | 78 --------- spine/usecase_test.go | 42 ----- 15 files changed, 463 insertions(+), 395 deletions(-) create mode 100644 spine/model/usecaseinformation_additions.go create mode 100644 spine/testdata/nm_usecaseinformationlistdata_recv_reply.json delete mode 100644 spine/usecase.go delete mode 100644 spine/usecase_manager.go delete mode 100644 spine/usecase_test.go diff --git a/spine/device.go b/spine/device.go index 97ad813e..b4944619 100644 --- a/spine/device.go +++ b/spine/device.go @@ -3,10 +3,9 @@ package spine import "github.com/enbility/eebus-go/spine/model" type DeviceImpl struct { - address *model.AddressDeviceType - dType *model.DeviceTypeType - featureSet *model.NetworkManagementFeatureSetType - useCaseManager *UseCaseManager + address *model.AddressDeviceType + dType *model.DeviceTypeType + featureSet *model.NetworkManagementFeatureSetType } // Initialize a new device @@ -15,8 +14,6 @@ type DeviceImpl struct { func NewDeviceImpl(address *model.AddressDeviceType, dType *model.DeviceTypeType, featureSet *model.NetworkManagementFeatureSetType) *DeviceImpl { deviceImpl := &DeviceImpl{} - deviceImpl.useCaseManager = NewUseCaseManager(deviceImpl) - if dType != nil { deviceImpl.dType = dType } @@ -36,10 +33,6 @@ func (r *DeviceImpl) Address() *model.AddressDeviceType { return r.address } -func (r *DeviceImpl) UseCaseManager() *UseCaseManager { - return r.useCaseManager -} - func (r *DeviceImpl) DeviceType() *model.DeviceTypeType { return r.dType } diff --git a/spine/device_local.go b/spine/device_local.go index df5647f6..65b57e07 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -281,6 +281,7 @@ func (r *DeviceLocalImpl) AddEntity(entity *EntityLocalImpl) { } func (r *DeviceLocalImpl) RemoveEntity(entity *EntityLocalImpl) { + entity.RemoveAllUseCaseSupports() entity.RemoveAllSubscriptions() entity.RemoveAllBindings() diff --git a/spine/device_remote.go b/spine/device_remote.go index 53656c37..16003b0f 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -243,15 +243,18 @@ func (d *DeviceRemoteImpl) VerifyUseCaseScenariosAndFeaturesSupport( scenarios []model.UseCaseScenarioSupportType, serverFeatures []model.FeatureTypeType, ) bool { - remoteUseCaseManager := d.UseCaseManager() + entity := d.Entity(DeviceInformationAddressEntity) - usecases := remoteUseCaseManager.UseCaseInformation() - if len(usecases) == 0 { + nodemgmt := d.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + + usecases := nodemgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + + if usecases == nil || len(usecases.UseCaseInformation) == 0 { return false } usecaseAndScenariosFound := false - for _, usecase := range usecases { + for _, usecase := range usecases.UseCaseInformation { if usecase.Actor == nil || *usecase.Actor != usecaseActor { continue } @@ -261,10 +264,6 @@ func (d *DeviceRemoteImpl) VerifyUseCaseScenariosAndFeaturesSupport( continue } - if support.UseCaseAvailable == nil || !*support.UseCaseAvailable { - continue - } - var foundScenarios []model.UseCaseScenarioSupportType for _, scenario := range support.ScenarioSupport { if slices.Contains(scenarios, scenario) { diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index 1e0b14ea..d7eb535f 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -5,10 +5,15 @@ import ( "time" "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) +const ( + nm_usecaseinformationlistdata_recv_reply_file_path = "../spine/testdata/nm_usecaseinformationlistdata_recv_reply.json" +) + func TestDeviceRemoteSuite(t *testing.T) { suite.Run(t, new(DeviceRemoteSuite)) } @@ -18,6 +23,7 @@ type DeviceRemoteSuite struct { localDevice *DeviceLocalImpl remoteDevice *DeviceRemoteImpl + remoteEntity *EntityRemoteImpl } func (s *DeviceRemoteSuite) WriteSpineMessage([]byte) {} @@ -30,14 +36,15 @@ func (s *DeviceRemoteSuite) BeforeTest(suiteName, testName string) { ski := "test" sender := NewSender(s) s.remoteDevice = NewDeviceRemoteImpl(s.localDevice, ski, sender) + s.remoteDevice.address = util.Ptr(model.AddressDeviceType("test")) s.localDevice.AddRemoteDevice(ski, s) - entity := NewEntityRemoteImpl(s.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) + s.remoteEntity = NewEntityRemoteImpl(s.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) - feature := NewFeatureRemoteImpl(0, entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - entity.AddFeature(feature) + feature := NewFeatureRemoteImpl(0, s.remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + s.remoteEntity.AddFeature(feature) - s.remoteDevice.AddEntity(entity) + s.remoteDevice.AddEntity(s.remoteEntity) } func (s *DeviceRemoteSuite) Test_RemoveByAddress() { @@ -73,6 +80,26 @@ func (s *DeviceRemoteSuite) Test_FeatureByEntityTypeAndRole() { assert.Nil(s.T(), feature) } +func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport_ElliJSON() { + _, _ = s.remoteDevice.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) + + result := s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeBatterySystem, + model.UseCaseNameTypeControlOfBattery, + []model.UseCaseScenarioSupportType{}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), false, result) + + result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + []model.UseCaseScenarioSupportType{}, + []model.FeatureTypeType{}, + ) + assert.Equal(s.T(), true, result) +} + func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { result := s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeEVSE, @@ -82,7 +109,24 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { ) assert.Equal(s.T(), false, result) - s.remoteDevice.UseCaseManager().Add( + nodeMgmtEntity := s.remoteDevice.Entity(DeviceInformationAddressEntity) + nodeMgmt := nodeMgmtEntity.Feature(util.Ptr(model.AddressFeatureType(NodeManagementFeatureId))) + + // initialize with empty data + newData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{}, + } + nodeMgmt.UpdateData(model.FunctionTypeNodeManagementUseCaseData, newData, nil, nil) + + data := nodeMgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + + address := model.FeatureAddressType{ + Device: s.remoteDevice.address, + Entity: s.remoteEntity.address.Entity, + } + + data.AddUseCaseSupport( + address, model.UseCaseActorTypeBatterySystem, model.UseCaseNameTypeControlOfBattery, model.SpecificationVersionType("1.0.0"), @@ -90,16 +134,19 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { true, []model.UseCaseScenarioSupportType{1}, ) + nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) + data = nodeMgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, - []model.UseCaseScenarioSupportType{}, - []model.FeatureTypeType{}, + nil, + nil, ) assert.Equal(s.T(), false, result) - s.remoteDevice.UseCaseManager().Add( + data.AddUseCaseSupport( + address, model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVCommissioningAndConfiguration, model.SpecificationVersionType("1.0.0"), @@ -107,16 +154,19 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { true, []model.UseCaseScenarioSupportType{1}, ) + nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) + data = nodeMgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, - []model.UseCaseScenarioSupportType{}, - []model.FeatureTypeType{}, + nil, + nil, ) assert.Equal(s.T(), false, result) - s.remoteDevice.UseCaseManager().Add( + data.AddUseCaseSupport( + address, model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, model.SpecificationVersionType("1.0.0"), @@ -124,16 +174,19 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { false, []model.UseCaseScenarioSupportType{1}, ) + nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) + data = nodeMgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, - []model.UseCaseScenarioSupportType{}, - []model.FeatureTypeType{}, + nil, + nil, ) - assert.Equal(s.T(), false, result) + assert.Equal(s.T(), true, result) - s.remoteDevice.UseCaseManager().Add( + data.AddUseCaseSupport( + address, model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, model.SpecificationVersionType("1.0.0"), @@ -141,12 +194,13 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { true, []model.UseCaseScenarioSupportType{1}, ) + nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, - []model.UseCaseScenarioSupportType{}, - []model.FeatureTypeType{}, + nil, + nil, ) assert.Equal(s.T(), true, result) @@ -154,7 +208,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, []model.UseCaseScenarioSupportType{2}, - []model.FeatureTypeType{}, + nil, ) assert.Equal(s.T(), false, result) @@ -162,7 +216,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { model.UseCaseActorTypeEVSE, model.UseCaseNameTypeEVSECommissioningAndConfiguration, []model.UseCaseScenarioSupportType{1}, - []model.FeatureTypeType{}, + nil, ) assert.Equal(s.T(), true, result) diff --git a/spine/entity_local.go b/spine/entity_local.go index 1b41ec5e..71d1ed58 100644 --- a/spine/entity_local.go +++ b/spine/entity_local.go @@ -84,14 +84,67 @@ func (r *EntityLocalImpl) Feature(addressFeature *model.AddressFeatureType) Feat } func (r *EntityLocalImpl) Information() *model.NodeManagementDetailedDiscoveryEntityInformationType { - res := model.NodeManagementDetailedDiscoveryEntityInformationType{ + res := &model.NodeManagementDetailedDiscoveryEntityInformationType{ Description: &model.NetworkManagementEntityDescriptionDataType{ EntityAddress: r.Address(), EntityType: &r.eType, }, } - return &res + return res +} + +// add a new usecase +func (r *EntityLocalImpl) AddUseCaseSupport( + actor model.UseCaseActorType, + useCaseName model.UseCaseNameType, + useCaseVersion model.SpecificationVersionType, + useCaseDocumemtSubRevision string, + useCaseAvailable bool, + scenarios []model.UseCaseScenarioSupportType, +) { + nodeMgmt := r.device.nodeManagement + + data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + + address := model.FeatureAddressType{ + Device: r.address.Device, + Entity: r.address.Entity, + } + + data.AddUseCaseSupport(address, actor, useCaseName, useCaseVersion, useCaseDocumemtSubRevision, useCaseAvailable, scenarios) + + nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) +} + +// Remove a usecase with a given actor ans usecase name +func (r *EntityLocalImpl) RemoveUseCaseSupport( + actor model.UseCaseActorType, + useCaseName model.UseCaseNameType, +) { + nodeMgmt := r.device.nodeManagement + + data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + + if data == nil { + return + } + + address := model.FeatureAddressType{ + Device: r.address.Device, + Entity: r.address.Entity, + } + + data.RemoveUseCaseSupport(address, actor, useCaseName) + + nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) +} + +// Remove all usecases +func (r *EntityLocalImpl) RemoveAllUseCaseSupports() { + r.RemoveUseCaseSupport("", "") +} + // Remove all subscriptions func (r *EntityLocalImpl) RemoveAllSubscriptions() { for _, item := range r.features { diff --git a/spine/feature_local.go b/spine/feature_local.go index e9447f76..84669ddd 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -190,6 +190,9 @@ func (r *FeatureLocalImpl) RequestAndFetchData( // Subscribe to a remote feature func (r *FeatureLocalImpl) Subscribe(remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { + if remoteAddress.Device == nil { + return nil, model.NewErrorTypeFromString("device not found") + } remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) if remoteDevice == nil { return nil, model.NewErrorTypeFromString("device not found") diff --git a/spine/function_data_factory.go b/spine/function_data_factory.go index f2a99328..2e0b1202 100644 --- a/spine/function_data_factory.go +++ b/spine/function_data_factory.go @@ -7,10 +7,6 @@ import ( ) func CreateFunctionData[F any](featureType model.FeatureTypeType) []F { - if featureType == model.FeatureTypeTypeNodeManagement { - return []F{} // NodeManagement implementation is not using function data - } - // Some devices use generic for everything (e.g. Vaillant Arotherm heatpump) // or for some things like the SMA HM 2.0 or Elli Wallbox, which uses Generic feature // for Heartbeats, even though that should go into FeatureTypeTypeDeviceDiagnosis @@ -18,6 +14,16 @@ func CreateFunctionData[F any](featureType model.FeatureTypeType) []F { var result []F + if featureType == model.FeatureTypeTypeNodeManagement { + result = []F{ + createFunctionData[model.NodeManagementDestinationListDataType, F](model.FunctionTypeNodeManagementDestinationListData), + createFunctionData[model.NodeManagementDetailedDiscoveryDataType, F](model.FunctionTypeNodeManagementDetailedDiscoveryData), + createFunctionData[model.NodeManagementUseCaseDataType, F](model.FunctionTypeNodeManagementUseCaseData), + } + + return result + } + if featureType == model.FeatureTypeTypeActuatorLevel || featureType == model.FeatureTypeTypeGeneric { result = append(result, []F{ createFunctionData[model.ActuatorLevelDataType, F](model.FunctionTypeActuatorLevelData), diff --git a/spine/function_data_factory_test.go b/spine/function_data_factory_test.go index af63826d..d51e4b19 100644 --- a/spine/function_data_factory_test.go +++ b/spine/function_data_factory_test.go @@ -87,7 +87,7 @@ func TestFunctionDataFactory_FunctionDataCmd(t *testing.T) { func TestFunctionDataFactory_NodeMgmtFeatureType(t *testing.T) { result := CreateFunctionData[FunctionDataCmd](model.FeatureTypeTypeNodeManagement) - assert.Equal(t, 0, len(result)) + assert.Equal(t, 3, len(result)) } func TestFunctionDataFactory_unknownFunctionDataType(t *testing.T) { diff --git a/spine/model/nodemanagement_additions.go b/spine/model/nodemanagement_additions.go index a3466f67..1ad61bca 100644 --- a/spine/model/nodemanagement_additions.go +++ b/spine/model/nodemanagement_additions.go @@ -1,5 +1,12 @@ package model +import ( + "reflect" + "sync" +) + +var nmMux sync.Mutex + // NodeManagementDestinationListDataType var _ Updater = (*NodeManagementDestinationListDataType)(nil) @@ -12,3 +19,125 @@ func (r *NodeManagementDestinationListDataType) UpdateList(newList any, filterPa r.NodeManagementDestinationData = UpdateList(r.NodeManagementDestinationData, newData, filterPartial, filterDelete) } + +// NodeManagementUseCaseDataType + +// find the matching UseCaseInformation index for +// a given FeatureAddressType, UseCaseActorType and UseCaseNameType +// +// if UseCaseActorType and UseCaseNameType are empty they are ignored, +// and the first matching UseCaseInformation item is returned +func (n *NodeManagementUseCaseDataType) useCaseInformationIndex( + address FeatureAddressType, + actor UseCaseActorType, + useCaseName UseCaseNameType, +) (int, bool) { + + // get the element with the same entity + for index, item := range n.UseCaseInformation { + if item.Address.Device == nil || + item.Address.Entity == nil || + !reflect.DeepEqual(item.Address.Device, address.Device) || + !reflect.DeepEqual(item.Address.Entity, address.Entity) { + continue + } + + if len(actor) == 0 && len(useCaseName) == 0 { + return index, true + } + + if len(actor) > 0 { + if item.Actor == nil || *item.Actor != actor { + continue + } + + } + + if len(useCaseName) == 0 { + return index, true + } + + if _, ok := item.useCaseSupportIndex(useCaseName); ok { + return index, true + } + } + + return -1, false +} + +// add a new UseCaseSupportType +func (n *NodeManagementUseCaseDataType) AddUseCaseSupport( + address FeatureAddressType, + actor UseCaseActorType, + useCaseName UseCaseNameType, + useCaseVersion SpecificationVersionType, + useCaseDocumemtSubRevision string, + useCaseAvailable bool, + scenarios []UseCaseScenarioSupportType, +) { + nmMux.Lock() + defer nmMux.Unlock() + + useCaseSupport := UseCaseSupportType{ + UseCaseName: &useCaseName, + UseCaseVersion: &useCaseVersion, + UseCaseAvailable: &useCaseAvailable, + ScenarioSupport: scenarios, + UseCaseDocumentSubRevision: &useCaseDocumemtSubRevision, + } + + // is there an entry for the entity address and actor + usecaseIndex, ok := n.useCaseInformationIndex(address, actor, "") + + if ok { + n.UseCaseInformation[usecaseIndex].Add(useCaseSupport) + } else { + // create a new element for this entity + useCaseInformation := UseCaseInformationDataType{ + Address: &FeatureAddressType{ + Device: address.Device, + Entity: address.Entity, + }, + Actor: &actor, + UseCaseSupport: []UseCaseSupportType{useCaseSupport}, + } + n.UseCaseInformation = append(n.UseCaseInformation, useCaseInformation) + } +} + +// Remove a UseCaseSupportType with +// a provided FeatureAddressType, UseCaseActorType and UseCaseNameType +func (n *NodeManagementUseCaseDataType) RemoveUseCaseSupport( + address FeatureAddressType, + actor UseCaseActorType, + useCaseName UseCaseNameType, +) { + nmMux.Lock() + defer nmMux.Unlock() + + // is there an entry for the entity address, actor and usecase name + usecaseIndex, ok := n.useCaseInformationIndex(address, actor, useCaseName) + if !ok { + return + } + + var usecaseInfo []UseCaseInformationDataType + + for index, item := range n.UseCaseInformation { + if index != usecaseIndex { + usecaseInfo = append(usecaseInfo, item) + continue + } + + item.Remove(useCaseName) + + // only add the item if there are any usecases left + if len(item.UseCaseSupport) == 0 { + continue + } + + usecaseInfo = append(usecaseInfo, item) + } + + n.UseCaseInformation = usecaseInfo +} diff --git a/spine/model/usecaseinformation_additions.go b/spine/model/usecaseinformation_additions.go new file mode 100644 index 00000000..685247af --- /dev/null +++ b/spine/model/usecaseinformation_additions.go @@ -0,0 +1,54 @@ +package model + +import "sync" + +var uciMux sync.Mutex + +// UseCaseInformationDataType + +// find the matching UseCaseSupport index for a UseCaseNameType +func (u *UseCaseInformationDataType) useCaseSupportIndex(useCaseName UseCaseNameType) (int, bool) { + // get the element with the same entity + for index, item := range u.UseCaseSupport { + if item.UseCaseName != nil && *item.UseCaseName == useCaseName { + return index, true + } + } + + return -1, false +} + +// add a new UseCaseSupportType +func (u *UseCaseInformationDataType) Add(useCase UseCaseSupportType) { + uciMux.Lock() + defer uciMux.Unlock() + + if useCase.UseCaseName == nil { + return + } + + // only add it if it does not exist yet + if _, ok := u.useCaseSupportIndex(*useCase.UseCaseName); ok { + return + } + + u.UseCaseSupport = append(u.UseCaseSupport, useCase) +} + +// remove a UseCaseSupportType with a given UseCaseNameType +func (u *UseCaseInformationDataType) Remove(useCaseName UseCaseNameType) { + uciMux.Lock() + defer uciMux.Unlock() + + var usecases []UseCaseSupportType + + for _, item := range u.UseCaseSupport { + if item.UseCaseName != nil && *item.UseCaseName != useCaseName { + continue + } + + usecases = append(usecases, item) + } + + u.UseCaseSupport = usecases +} diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go index f21c6e63..626c6641 100644 --- a/spine/nodemanagement_usecase.go +++ b/spine/nodemanagement_usecase.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" - "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" ) @@ -23,86 +22,20 @@ func (r *NodeManagementImpl) NotifyUseCaseData(remoteDevice *DeviceRemoteImpl) ( featureRemote := remoteDevice.FeatureByEntityTypeAndRole(rEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) - cmd := model.CmdType{ - NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ - UseCaseInformation: r.entity.Device().UseCaseManager().UseCaseInformation(), - }, - } + fd := r.functionData(model.FunctionTypeNodeManagementUseCaseData) + cmd := fd.NotifyCmdType(nil, nil, false, nil) return featureRemote.Sender().Notify(r.Address(), rfAdress, cmd) } func (r *NodeManagementImpl) processReadUseCaseData(featureRemote *FeatureRemoteImpl, requestHeader *model.HeaderType) error { - - cmd := model.CmdType{ - NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ - UseCaseInformation: r.entity.Device().UseCaseManager().UseCaseInformation(), - }, - } + cmd := r.functionData(model.FunctionTypeNodeManagementUseCaseData).ReplyCmdType(false) return featureRemote.Sender().Reply(requestHeader, r.Address(), cmd) } -func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data model.NodeManagementUseCaseDataType) error { - useCaseInformation := data.UseCaseInformation - if useCaseInformation == nil { - return errors.New("nodemanagement.replyUseCaseData: invalid UseCaseInformation") - } - - remoteUseCaseManager := message.FeatureRemote.Device().UseCaseManager() - remoteUseCaseManager.RemoveAll() - - for _, useCaseInfo := range useCaseInformation { - // this is mandatory - var actor model.UseCaseActorType - if useCaseInfo.Actor != nil { - actor = model.UseCaseActorType(*useCaseInfo.Actor) - } else { - logging.Log().Debug("actor is missing in useCaseInformation") - break - } - - for _, useCaseSupport := range useCaseInfo.UseCaseSupport { - - // this is mandatory - var useCaseName model.UseCaseNameType - if useCaseSupport.UseCaseName != nil { - useCaseName = model.UseCaseNameType(*useCaseSupport.UseCaseName) - } else { - logging.Log().Debug("useCaseName is missing in useCaseSupport") - continue - } - - // this is optional - useCaseAvailable := true - if useCaseSupport.UseCaseAvailable != nil { - useCaseAvailable = *useCaseSupport.UseCaseAvailable - } - - var useCaseVersion model.SpecificationVersionType - if useCaseSupport.UseCaseVersion != nil { - useCaseVersion = model.SpecificationVersionType(*useCaseSupport.UseCaseVersion) - } - - var useCaseDocumemtSubRevision string - if useCaseSupport.UseCaseDocumentSubRevision != nil { - useCaseDocumemtSubRevision = *useCaseSupport.UseCaseDocumentSubRevision - } - - if useCaseSupport.ScenarioSupport == nil { - logging.Log().Errorf("scenarioSupport is missing in useCaseSupport %s", useCaseName) - continue - } - - remoteUseCaseManager.Add( - actor, - useCaseName, - useCaseVersion, - useCaseDocumemtSubRevision, - useCaseAvailable, - useCaseSupport.ScenarioSupport) - } - } +func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data *model.NodeManagementUseCaseDataType) error { + message.FeatureRemote.UpdateData(model.FunctionTypeNodeManagementUseCaseData, data, nil, nil) // the data was updated, so send an event, other event handlers may watch out for this as well payload := EventPayload{ @@ -129,10 +62,10 @@ func (r *NodeManagementImpl) handleMsgUseCaseData(message *Message, data *model. if err := r.pendingRequests.Remove(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference); err != nil { return errors.New(err.String()) } - return r.processReplyUseCaseData(message, *data) + return r.processReplyUseCaseData(message, data) case model.CmdClassifierTypeNotify: - return r.processReplyUseCaseData(message, *data) + return r.processReplyUseCaseData(message, data) default: return fmt.Errorf("nodemanagement.handleUseCaseData: NodeManagementUseCaseData CmdClassifierType not implemented: %s", message.CmdClassifier) diff --git a/spine/testdata/nm_usecaseinformationlistdata_recv_reply.json b/spine/testdata/nm_usecaseinformationlistdata_recv_reply.json new file mode 100644 index 00000000..14d86965 --- /dev/null +++ b/spine/testdata/nm_usecaseinformationlistdata_recv_reply.json @@ -0,0 +1,120 @@ +{ + "datagram": { + "header": { + "specificationVersion": "1.3.0", + "addressSource": { + "device": "TestDeviceAddress", + "entity": [ + 0 + ], + "feature": 0 + }, + "addressDestination": { + "device": "HEMS", + "entity": [ + 0 + ], + "feature": 0 + }, + "msgCounter": 2, + "msgCounterReference": 1, + "cmdClassifier": "reply" + }, + "payload": { + "cmd": [ + { + "nodeManagementUseCaseData": { + "useCaseInformation": [ + { + "address": { + "device": "d:_i:47859_Elli-Wallbox-xxxxxxxxxx" + }, + "actor": "EVSE", + "useCaseSupport": [ + { + "useCaseName": "evseCommissioningAndConfiguration", + "useCaseVersion": "1.0.1", + "scenarioSupport": [ + 1, + 2 + ] + }, + { + "useCaseName": "evChargingSummary", + "useCaseVersion": "1.0.1", + "scenarioSupport": [ + 1 + ] + } + ] + }, + { + "address": { + "device": "d:_i:47859_Elli-Wallbox-2041A0ZW5R" + }, + "actor": "EV", + "useCaseSupport": [ + { + "useCaseName": "evCommissioningAndConfiguration", + "useCaseVersion": "1.0.1", + "scenarioSupport": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ] + }, + { + "useCaseName": "overloadProtectionByEvChargingCurrentCurtailment", + "useCaseVersion": "1.0.1", + "scenarioSupport": [ + 1, + 2, + 3 + ] + }, + { + "useCaseName": "coordinatedEvCharging", + "useCaseVersion": "1.0.1", + "scenarioSupport": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ] + }, + { + "useCaseName": "measurementOfElectricityDuringEvCharging", + "useCaseVersion": "1.0.1", + "scenarioSupport": [ + 1, + 2, + 3 + ] + }, + { + "useCaseName": "optimizationOfSelfConsumptionDuringEvCharging", + "useCaseVersion": "1.0.1", + "scenarioSupport": [ + 1, + 2, + 3 + ] + } + ] + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/spine/usecase.go b/spine/usecase.go deleted file mode 100644 index b28be496..00000000 --- a/spine/usecase.go +++ /dev/null @@ -1,157 +0,0 @@ -package spine - -import ( - "fmt" - - "github.com/ahmetb/go-linq/v3" - "github.com/enbility/eebus-go/spine/model" -) - -// a default mapping of a given EntityTypeType to a UseCaseActorType -var entityTypeActorMap = map[model.EntityTypeType]model.UseCaseActorType{ - model.EntityTypeTypeBattery: model.UseCaseActorTypeBattery, - model.EntityTypeTypeCEM: model.UseCaseActorTypeCEM, - model.EntityTypeTypeCompressor: model.UseCaseActorTypeCompressor, - model.EntityTypeTypeElectricityStorageSystem: model.UseCaseActorTypeBatterySystem, - model.EntityTypeTypeElectricityGenerationSystem: model.UseCaseActorTypePVSystem, - model.EntityTypeTypeEV: model.UseCaseActorTypeEV, - model.EntityTypeTypeEVSE: model.UseCaseActorTypeEVSE, - model.EntityTypeTypeDHWCircuit: model.UseCaseActorTypeDHWCircuit, - model.EntityTypeTypeHeatingCircuit: model.UseCaseActorTypeHeatingCircuit, - model.EntityTypeTypeHeatPumpAppliance: model.UseCaseActorTypeHeatPump, - model.EntityTypeTypeHvacRoom: model.UseCaseActorTypeHVACRoom, - model.EntityTypeTypeInverter: model.UseCaseActorTypeInverter, - model.EntityTypeTypeSmartEnergyAppliance: model.UseCaseActorTypeControllableSystem, - model.EntityTypeTypeSubMeterElectricity: model.UseCaseActorTypeControllableSystem, - model.EntityTypeTypeGridConnectionPointOfPremises: model.UseCaseActorTypeGridConnectionPoint, -} - -// list of known use cases and the allowed actors for each -var useCaseValidActorsMap = map[model.UseCaseNameType][]model.UseCaseActorType{ - model.UseCaseNameTypeConfigurationOfDhwSystemFunction: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeDHWCircuit}, - model.UseCaseNameTypeConfigurationOfDhwTemperature: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeDHWCircuit}, - model.UseCaseNameTypeConfigurationOfRoomCoolingSystemFunction: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeHVACRoom}, - model.UseCaseNameTypeConfigurationOfRoomCoolingTemperature: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeHVACRoom}, - model.UseCaseNameTypeConfigurationOfRoomHeatingSystemFunction: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeHVACRoom}, - model.UseCaseNameTypeConfigurationOfRoomHeatingTemperature: {model.UseCaseActorTypeConfigurationAppliance, model.UseCaseActorTypeHVACRoom}, - model.UseCaseNameTypeControlOfBattery: {model.UseCaseActorTypeInverter, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeCoordinatedEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM, model.UseCaseActorTypeEnergyBroker}, - model.UseCaseNameTypeEVChargingSummary: {model.UseCaseActorTypeEVSE, model.UseCaseActorTypeCEM, model.UseCaseActorTypeEnergyBroker}, - model.UseCaseNameTypeEVCommissioningAndConfiguration: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeEVSECommissioningAndConfiguration: {model.UseCaseActorTypeEVSE, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeEVStateOfCharge: {model.UseCaseActorTypeEV, model.UseCaseActorTypeMonitoringAppliance}, - model.UseCaseNameTypeFlexibleLoad: {model.UseCaseActorTypeEnergyConsumer, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeFlexibleStartForWhiteGoods: {model.UseCaseActorTypeSmartAppliance, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeIncentiveTableBasedPowerConsumptionManagement: {model.UseCaseActorTypeEnergyConsumer, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeLimitationOfPowerConsumption: {model.UseCaseActorTypeEnergyGuard, model.UseCaseActorTypeControllableSystem}, - model.UseCaseNameTypeLimitationOfPowerProduction: {model.UseCaseActorTypeEnergyGuard, model.UseCaseActorTypeControllableSystem}, - model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeMonitoringAndControlOfSmartGridReadyConditions: {model.UseCaseActorTypeHeatPump, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeMonitoringOfBattery: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeBattery}, - model.UseCaseNameTypeMonitoringOfDhwSystemFunction: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeDHWCircuit}, - model.UseCaseNameTypeMonitoringOfDhwTemperature: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeDHWCircuit}, - model.UseCaseNameTypeMonitoringOfGridConnectionPoint: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeGridConnectionPoint}, - model.UseCaseNameTypeMonitoringOfInverter: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeInverter}, - model.UseCaseNameTypeMonitoringOfOutdoorTemperature: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeOutdoorTemperatureSensor}, - model.UseCaseNameTypeMonitoringOfPowerConsumption: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeMonitoredUnit}, - model.UseCaseNameTypeMonitoringOfPvString: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypePVString}, - model.UseCaseNameTypeMonitoringOfRoomCoolingSystemFunction: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeHVACRoom}, - model.UseCaseNameTypeMonitoringOfRoomHeatingSystemFunction: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeHVACRoom}, - model.UseCaseNameTypeMonitoringOfRoomTemperature: {model.UseCaseActorTypeMonitoringAppliance, model.UseCaseActorTypeHVACRoom}, - model.UseCaseNameTypeOptimizationOfSelfConsumptionByHeatPumpCompressorFlexibility: {model.UseCaseActorTypeCompressor, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM}, - model.UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment: {model.UseCaseActorTypeEV, model.UseCaseActorTypeCEM, model.UseCaseActorTypeEnergyGuard}, - model.UseCaseNameTypeVisualizationOfAggregatedBatteryData: {model.UseCaseActorTypeVisualizationAppliance, model.UseCaseActorTypeBatterySystem}, - model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData: {model.UseCaseActorTypeVisualizationAppliance, model.UseCaseActorTypePVSystem}, - model.UseCaseNameTypeVisualizationOfHeatingAreaName: {model.UseCaseActorTypeVisualizationAppliance, model.UseCaseActorTypeHeatingCircuit, model.UseCaseActorTypeHeatingZone, model.UseCaseActorTypeHVACRoom}, -} - -// defines a specific usecase implementation -// right now this is just used as a wrapper for supported usecases -type UseCaseImpl struct { - Entity *EntityLocalImpl - Actor model.UseCaseActorType - - name model.UseCaseNameType - useCaseVersion model.SpecificationVersionType - useCaseAvailable bool - scenarioSupport []model.UseCaseScenarioSupportType -} - -// returns a UseCaseImpl with a default mapping of entity to actor using data -func NewUseCase( - entity *EntityLocalImpl, - ucEnumType model.UseCaseNameType, - useCaseVersion model.SpecificationVersionType, - useCaseDocumemtSubRevision string, - useCaseAvailable bool, - scenarioSupport []model.UseCaseScenarioSupportType, -) *UseCaseImpl { - checkEntityArguments(*entity.EntityImpl) - - actor := entityTypeActorMap[entity.EntityType()] - - return NewUseCaseWithActor(entity, actor, ucEnumType, useCaseVersion, useCaseDocumemtSubRevision, useCaseAvailable, scenarioSupport) -} - -// returns a UseCaseImpl with specific entity and actor -func NewUseCaseWithActor( - entity *EntityLocalImpl, - actor model.UseCaseActorType, - ucEnumType model.UseCaseNameType, - useCaseVersion model.SpecificationVersionType, - useCaseDocumemtSubRevision string, - useCaseAvailable bool, - scenarioSupport []model.UseCaseScenarioSupportType, -) *UseCaseImpl { - checkUCArguments(actor, ucEnumType) - - ucManager := entity.Device().UseCaseManager() - ucManager.Add(actor, ucEnumType, useCaseVersion, useCaseDocumemtSubRevision, useCaseAvailable, scenarioSupport) - - return &UseCaseImpl{ - Entity: entity, - Actor: actor, - name: model.UseCaseNameType(ucEnumType), - useCaseVersion: useCaseVersion, - useCaseAvailable: useCaseAvailable, - scenarioSupport: scenarioSupport, - } -} - -// check if there is an predefined mapping available -func checkEntityArguments(entity EntityImpl) { - actor := entityTypeActorMap[entity.EntityType()] - if actor == "" { - panic(fmt.Errorf("cannot derive actor for entity type '%s'", entity.EntityType())) - } -} - -// check if the actor is valid for the given usecase type -func checkUCArguments(actor model.UseCaseActorType, ucEnumType model.UseCaseNameType) { - if !linq.From(useCaseValidActorsMap[ucEnumType]).Contains(actor) { - panic(fmt.Errorf("the actor '%s' is not valid for the use case '%s'", actor, ucEnumType)) - } -} - -// Update the availability of this usecase and -// trigger a notification being sent to the remote device -func (u *UseCaseImpl) SetUseCaseAvailable(available bool) { - u.useCaseAvailable = available - - u.Entity.Device().NotifyUseCaseData() -} - -/* -// This is not yet used, might be removed? -func waitForRequest[T any](c chan T, maxDelay time.Duration) *T { - timeout := time.After(maxDelay) - - select { - case data := <-c: - return &data - case <-timeout: - return nil - } -} -*/ diff --git a/spine/usecase_manager.go b/spine/usecase_manager.go deleted file mode 100644 index d4d24e29..00000000 --- a/spine/usecase_manager.go +++ /dev/null @@ -1,78 +0,0 @@ -package spine - -import ( - "github.com/enbility/eebus-go/spine/model" -) - -// manages the supported usecases for a device -// each device has its own UseCaseManager -type UseCaseManager struct { - useCaseInformationMap map[model.UseCaseActorType][]model.UseCaseSupportType - - localDevice *DeviceImpl -} - -// return a new UseCaseManager -func NewUseCaseManager(localDevice *DeviceImpl) *UseCaseManager { - return &UseCaseManager{ - useCaseInformationMap: make(map[model.UseCaseActorType][]model.UseCaseSupportType), - localDevice: localDevice, - } -} - -// add a usecase -func (r *UseCaseManager) Add( - actor model.UseCaseActorType, - useCaseName model.UseCaseNameType, - useCaseVersion model.SpecificationVersionType, - useCaseDocumemtSubRevision string, - useCaseAvailable bool, - scenarios []model.UseCaseScenarioSupportType, -) { - useCaseSupport := model.UseCaseSupportType{ - UseCaseVersion: &useCaseVersion, - UseCaseName: &useCaseName, - UseCaseAvailable: &useCaseAvailable, - ScenarioSupport: scenarios, - } - - if len(useCaseDocumemtSubRevision) > 0 { - useCaseSupport.UseCaseDocumentSubRevision = &useCaseDocumemtSubRevision - } - - useCaseInfo, exists := r.useCaseInformationMap[actor] - if !exists { - useCaseInfo = make([]model.UseCaseSupportType, 0) - } - useCaseInfo = append(useCaseInfo, useCaseSupport) - - r.useCaseInformationMap[actor] = useCaseInfo -} - -// this needs to be called when a new notification or reply will provide a new set of UseCases -func (r *UseCaseManager) RemoveAll() { - r.useCaseInformationMap = make(map[model.UseCaseActorType][]model.UseCaseSupportType) -} - -// return all actors and their supported usecases -func (r *UseCaseManager) UseCaseInformation() []model.UseCaseInformationDataType { - var result []model.UseCaseInformationDataType - - for actor, useCaseSupport := range r.useCaseInformationMap { - thisActor := actor - // according to ProtocolSpecification Version 1.3.0 chapter 7.5.2 - // the address is mandatory. At least the device address should be shown, - // preferably the entity and features as well - deviceAddress := &model.FeatureAddressType{ - Device: r.localDevice.Address(), - } - useCaseInfo := model.UseCaseInformationDataType{ - Address: deviceAddress, - Actor: &thisActor, - UseCaseSupport: useCaseSupport, - } - result = append(result, useCaseInfo) - } - - return result -} diff --git a/spine/usecase_test.go b/spine/usecase_test.go deleted file mode 100644 index 415795e2..00000000 --- a/spine/usecase_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestUsecaseSuite(t *testing.T) { - suite.Run(t, new(UsecaseSuite)) -} - -type UsecaseSuite struct { - suite.Suite - - device *DeviceLocalImpl - entity *EntityLocalImpl -} - -func (s *UsecaseSuite) BeforeTest(suiteName, testName string) { - s.device = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - s.entity = NewEntityLocalImpl(s.device, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) - s.device.AddEntity(s.entity) - -} - -func (s *UsecaseSuite) Test_UseCase() { - uc := NewUseCase( - s.entity, - model.UseCaseNameTypeControlOfBattery, - model.SpecificationVersionType("1.0.0"), - "", - true, - []model.UseCaseScenarioSupportType{1}, - ) - assert.NotNil(s.T(), uc) - - uc.SetUseCaseAvailable(true) -} From 79271ff7478b10876f13e1ea7964e24b992de919 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 12:27:57 +0100 Subject: [PATCH 138/240] Add usecaseinformation tests and fix bug --- spine/model/usecaseinformation_additions.go | 3 +- .../usecaseinformation_additions_test.go | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 spine/model/usecaseinformation_additions_test.go diff --git a/spine/model/usecaseinformation_additions.go b/spine/model/usecaseinformation_additions.go index 685247af..d30bc5af 100644 --- a/spine/model/usecaseinformation_additions.go +++ b/spine/model/usecaseinformation_additions.go @@ -44,10 +44,9 @@ func (u *UseCaseInformationDataType) Remove(useCaseName UseCaseNameType) { for _, item := range u.UseCaseSupport { if item.UseCaseName != nil && *item.UseCaseName != useCaseName { - continue + usecases = append(usecases, item) } - usecases = append(usecases, item) } u.UseCaseSupport = usecases diff --git a/spine/model/usecaseinformation_additions_test.go b/spine/model/usecaseinformation_additions_test.go new file mode 100644 index 00000000..b91af46b --- /dev/null +++ b/spine/model/usecaseinformation_additions_test.go @@ -0,0 +1,47 @@ +package model + +import ( + "testing" + + "github.com/enbility/eebus-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestUseCaseInformationDataTypeSuite(t *testing.T) { + suite.Run(t, new(UseCaseInformationDataTypeSuite)) +} + +type UseCaseInformationDataTypeSuite struct { + suite.Suite +} + +func (s *UseCaseInformationDataTypeSuite) SetupSuite() {} +func (s *UseCaseInformationDataTypeSuite) TearDownTest() {} + +func (s *UseCaseInformationDataTypeSuite) BeforeTest(suiteName, testName string) {} + +func (s *UseCaseInformationDataTypeSuite) Test_AdditionsAndRemovals() { + ucs := &UseCaseInformationDataType{} + assert.NotNil(s.T(), ucs) + assert.Equal(s.T(), 0, len(ucs.UseCaseSupport)) + + uc := UseCaseSupportType{} + ucs.Add(uc) + assert.Equal(s.T(), 0, len(ucs.UseCaseSupport)) + + uc = UseCaseSupportType{ + UseCaseName: util.Ptr(UseCaseNameTypeControlOfBattery), + } + ucs.Add(uc) + assert.Equal(s.T(), 1, len(ucs.UseCaseSupport)) + + ucs.Add(uc) + assert.Equal(s.T(), 1, len(ucs.UseCaseSupport)) + + ucs.Remove(UseCaseNameTypeCoordinatedEVCharging) + assert.Equal(s.T(), 1, len(ucs.UseCaseSupport)) + + ucs.Remove(UseCaseNameTypeControlOfBattery) + assert.Equal(s.T(), 0, len(ucs.UseCaseSupport)) +} From d64f0964734ce11dae64fa14f93a7559b3fe29f6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 12:48:27 +0100 Subject: [PATCH 139/240] Add NodeManagementUseCaseDataType tests --- spine/model/nodemanagement_additions_test.go | 123 +++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 spine/model/nodemanagement_additions_test.go diff --git a/spine/model/nodemanagement_additions_test.go b/spine/model/nodemanagement_additions_test.go new file mode 100644 index 00000000..8a402eec --- /dev/null +++ b/spine/model/nodemanagement_additions_test.go @@ -0,0 +1,123 @@ +package model + +import ( + "testing" + + "github.com/enbility/eebus-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestNodeManagementUseCaseDataTypeSuite(t *testing.T) { + suite.Run(t, new(NodeManagementUseCaseDataTypeSuite)) +} + +type NodeManagementUseCaseDataTypeSuite struct { + suite.Suite +} + +func (s *NodeManagementUseCaseDataTypeSuite) SetupSuite() {} +func (s *NodeManagementUseCaseDataTypeSuite) TearDownTest() {} + +func (s *NodeManagementUseCaseDataTypeSuite) BeforeTest(suiteName, testName string) {} + +func (s *NodeManagementUseCaseDataTypeSuite) Test_AdditionsAndRemovals() { + ucs := &NodeManagementUseCaseDataType{} + assert.NotNil(s.T(), ucs) + assert.Equal(s.T(), 0, len(ucs.UseCaseInformation)) + + address := FeatureAddressType{ + Device: util.Ptr(AddressDeviceType("test")), + Entity: []AddressEntityType{1}, + } + ucs.AddUseCaseSupport( + address, + UseCaseActorTypeCEM, + UseCaseNameTypeControlOfBattery, + SpecificationVersionType(""), + "", + true, + []UseCaseScenarioSupportType{}, + ) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation[0].UseCaseSupport)) + + ucs.AddUseCaseSupport( + address, + UseCaseActorTypeCEM, + UseCaseNameTypeEVSECommissioningAndConfiguration, + SpecificationVersionType(""), + "", + true, + []UseCaseScenarioSupportType{}, + ) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) + assert.Equal(s.T(), 2, len(ucs.UseCaseInformation[0].UseCaseSupport)) + + ucs.AddUseCaseSupport( + address, + UseCaseActorTypeCEM, + UseCaseNameTypeEVSECommissioningAndConfiguration, + SpecificationVersionType(""), + "", + true, + []UseCaseScenarioSupportType{}, + ) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) + assert.Equal(s.T(), 2, len(ucs.UseCaseInformation[0].UseCaseSupport)) + + ucs.AddUseCaseSupport( + address, + UseCaseActorTypeEnergyGuard, + UseCaseNameTypeLimitationOfPowerConsumption, + SpecificationVersionType(""), + "", + true, + []UseCaseScenarioSupportType{}, + ) + assert.Equal(s.T(), 2, len(ucs.UseCaseInformation)) + assert.Equal(s.T(), 2, len(ucs.UseCaseInformation[0].UseCaseSupport)) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation[1].UseCaseSupport)) + + ucs.RemoveUseCaseSupport( + address, + UseCaseActorTypeCEM, + UseCaseNameTypeEVChargingSummary, + ) + assert.Equal(s.T(), 2, len(ucs.UseCaseInformation)) + assert.Equal(s.T(), 2, len(ucs.UseCaseInformation[0].UseCaseSupport)) + + ucs.RemoveUseCaseSupport( + address, + UseCaseActorTypeCEM, + UseCaseNameTypeControlOfBattery, + ) + assert.Equal(s.T(), 2, len(ucs.UseCaseInformation)) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation[0].UseCaseSupport)) + + ucs.RemoveUseCaseSupport( + address, + UseCaseActorTypeCEM, + UseCaseNameTypeEVSECommissioningAndConfiguration, + ) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) + + ucs.RemoveUseCaseSupport( + address, + "", + "", + ) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) + + invalidAddress := FeatureAddressType{ + Device: util.Ptr(AddressDeviceType("test")), + Entity: []AddressEntityType{2}, + } + ucs.RemoveUseCaseSupport( + invalidAddress, + UseCaseActorTypeCEM, + UseCaseNameTypeEVSECommissioningAndConfiguration, + ) + assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) + +} From a44e0cf71ab8754fabc4939d6168698006aee686 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 13:15:48 +0100 Subject: [PATCH 140/240] Fix bug in local entity and add (dummy) tests --- spine/entity_local.go | 4 +++- spine/entity_local_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/spine/entity_local.go b/spine/entity_local.go index 71d1ed58..0c4aacbd 100644 --- a/spine/entity_local.go +++ b/spine/entity_local.go @@ -106,6 +106,9 @@ func (r *EntityLocalImpl) AddUseCaseSupport( nodeMgmt := r.device.nodeManagement data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + if data == nil { + data = &model.NodeManagementUseCaseDataType{} + } address := model.FeatureAddressType{ Device: r.address.Device, @@ -125,7 +128,6 @@ func (r *EntityLocalImpl) RemoveUseCaseSupport( nodeMgmt := r.device.nodeManagement data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - if data == nil { return } diff --git a/spine/entity_local_test.go b/spine/entity_local_test.go index b48668ad..ae680d01 100644 --- a/spine/entity_local_test.go +++ b/spine/entity_local_test.go @@ -53,4 +53,28 @@ func (suite *EntityLocalTestSuite) Test_Entity() { assert.NotNil(suite.T(), f4) assert.Equal(suite.T(), 3, len(entity.Features())) + + entity.RemoveAllUseCaseSupports() + + entity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + model.SpecificationVersionType("1.0.0"), + "", + true, + []model.UseCaseScenarioSupportType{1, 2}, + ) + + entity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + model.UseCaseNameTypeEVSECommissioningAndConfiguration, + model.SpecificationVersionType("1.0.0"), + "", + true, + []model.UseCaseScenarioSupportType{1, 2}, + ) + + entity.RemoveAllUseCaseSupports() + entity.RemoveAllBindings() + entity.RemoveAllSubscriptions() } From 8af5ba5eccea7248a14159a5b3dbc2bdcedd32b7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 18:31:30 +0100 Subject: [PATCH 141/240] Introduce FeatureRemote interface --- features/deviceclassification.go | 2 +- features/deviceconfiguration.go | 4 +-- features/devicediagnosis.go | 2 +- features/electricalconnection.go | 6 ++--- features/feature.go | 4 +-- features/identification.go | 2 +- features/incentivetable.go | 6 ++--- features/loadcontrol.go | 4 +-- features/measurement.go | 6 ++--- features/timeseries.go | 6 ++--- .../electricalconnection_test.go | 6 ++--- integration_tests/measurement_test.go | 8 +++--- spine/api.go | 24 +++++++++++++---- spine/binding_manager.go | 4 +-- spine/device_remote.go | 27 ++++++++++--------- spine/device_remote_test.go | 8 +++--- spine/entity_remote.go | 8 +++--- spine/events.go | 2 +- spine/feature_local.go | 16 +++++------ spine/feature_local_test.go | 4 +-- spine/feature_remote.go | 5 +++- spine/message.go | 4 +-- spine/nodemanagement_destinationlist.go | 2 +- spine/nodemanagement_usecase.go | 2 +- spine/subscription_manager.go | 4 +-- 25 files changed, 93 insertions(+), 73 deletions(-) diff --git a/features/deviceclassification.go b/features/deviceclassification.go index 4cbfe850..4292ef6e 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -29,7 +29,7 @@ func (d *DeviceClassification) RequestManufacturerDetails() (*model.MsgCounterTy // get the current manufacturer details for a remote device entity func (d *DeviceClassification) GetManufacturerDetails() (*model.DeviceClassificationManufacturerDataType, error) { - rData := d.featureRemote.Data(model.FunctionTypeDeviceClassificationManufacturerData) + rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceClassificationManufacturerData) if rData == nil { return nil, ErrDataNotAvailable } diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index 5247302c..8fcc203d 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -35,7 +35,7 @@ func (d *DeviceConfiguration) RequestKeyValues() (*model.MsgCounterType, error) // return current descriptions for Device Configuration func (d *DeviceConfiguration) GetDescriptions() ([]model.DeviceConfigurationKeyValueDescriptionDataType, error) { - rData := d.featureRemote.Data(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData) + rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData) if rData == nil { return nil, ErrDataNotAvailable } @@ -86,7 +86,7 @@ func (d *DeviceConfiguration) GetDescriptionForKeyName(keyName model.DeviceConfi // return current values for Device Configuration func (d *DeviceConfiguration) GetKeyValues() ([]model.DeviceConfigurationKeyValueDataType, error) { - rData := d.featureRemote.Data(model.FunctionTypeDeviceConfigurationKeyValueListData) + rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceConfigurationKeyValueListData) if rData == nil { return nil, ErrDataNotAvailable } diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index a9261056..7ce673ef 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -29,7 +29,7 @@ func (d *DeviceDiagnosis) RequestState() (*model.MsgCounterType, error) { // get the current diagnosis state for an device entity func (d *DeviceDiagnosis) GetState() (*model.DeviceDiagnosisStateDataType, error) { - rData := d.featureRemote.Data(model.FunctionTypeDeviceDiagnosisStateData) + rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceDiagnosisStateData) if rData == nil { return nil, ErrDataNotAvailable } diff --git a/features/electricalconnection.go b/features/electricalconnection.go index 8c07b6be..72f0e17f 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -43,7 +43,7 @@ func (e *ElectricalConnection) RequestPermittedValueSets() (*model.MsgCounterTyp // return list of description for Electrical Connection func (e *ElectricalConnection) GetDescriptions() ([]model.ElectricalConnectionDescriptionDataType, error) { - rData := e.featureRemote.Data(model.FunctionTypeElectricalConnectionDescriptionListData) + rData := e.featureRemote.DataCopy(model.FunctionTypeElectricalConnectionDescriptionListData) if rData == nil { return nil, ErrMetadataNotAvailable } @@ -82,7 +82,7 @@ func (e *ElectricalConnection) GetDescriptionForMeasurementId(measurementId mode // return parameter descriptions for all Electrical Connections func (e *ElectricalConnection) GetParameterDescriptions() ([]model.ElectricalConnectionParameterDescriptionDataType, error) { - rData := e.featureRemote.Data(model.FunctionTypeElectricalConnectionParameterDescriptionListData) + rData := e.featureRemote.DataCopy(model.FunctionTypeElectricalConnectionParameterDescriptionListData) if rData == nil { return nil, ErrDataNotAvailable } @@ -168,7 +168,7 @@ func (e *ElectricalConnection) GetParameterDescriptionForMeasuredPhase(phase mod // return permitted values for all Electrical Connections func (e *ElectricalConnection) GetPermittedValueSets() ([]model.ElectricalConnectionPermittedValueSetDataType, error) { - rData := e.featureRemote.Data(model.FunctionTypeElectricalConnectionPermittedValueSetListData) + rData := e.featureRemote.DataCopy(model.FunctionTypeElectricalConnectionPermittedValueSetListData) if rData == nil { return nil, ErrDataNotAvailable } diff --git a/features/feature.go b/features/feature.go index 87902ab8..edb06d11 100644 --- a/features/feature.go +++ b/features/feature.go @@ -22,7 +22,7 @@ type FeatureImpl struct { localEntity *spine.EntityLocalImpl featureLocal spine.FeatureLocal - featureRemote *spine.FeatureRemoteImpl + featureRemote spine.FeatureRemote remoteDevice *spine.DeviceRemoteImpl remoteEntity *spine.EntityRemoteImpl @@ -96,7 +96,7 @@ func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, el } // internal helper method for getting local and remote feature for a given featureType and a given remoteDevice -func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (spine.FeatureLocal, *spine.FeatureRemoteImpl, error) { +func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (spine.FeatureLocal, spine.FeatureRemote, error) { if f.remoteEntity == nil { return nil, nil, errors.New("invalid remote entity provided") } diff --git a/features/identification.go b/features/identification.go index bf925fa4..ccf840ab 100644 --- a/features/identification.go +++ b/features/identification.go @@ -29,7 +29,7 @@ func (i *Identification) RequestValues() (*model.MsgCounterType, error) { // return current values for Identification func (i *Identification) GetValues() ([]model.IdentificationDataType, error) { - rData := i.featureRemote.Data(model.FunctionTypeIdentificationListData) + rData := i.featureRemote.DataCopy(model.FunctionTypeIdentificationListData) if rData == nil { return nil, ErrDataNotAvailable } diff --git a/features/incentivetable.go b/features/incentivetable.go index c31294f4..b47b2764 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -57,7 +57,7 @@ func (i *IncentiveTable) WriteValues(data []model.IncentiveTableType) (*model.Ms // return current values for Time Series func (i *IncentiveTable) GetValues() ([]model.IncentiveTableType, error) { - rData := i.featureRemote.Data(model.FunctionTypeIncentiveTableData) + rData := i.featureRemote.DataCopy(model.FunctionTypeIncentiveTableData) if rData == nil { return nil, ErrDataNotAvailable } @@ -88,7 +88,7 @@ func (i *IncentiveTable) WriteDescriptions(data []model.IncentiveTableDescriptio // return list of descriptions func (i *IncentiveTable) GetDescriptions() ([]model.IncentiveTableDescriptionType, error) { - rData := i.featureRemote.Data(model.FunctionTypeIncentiveTableDescriptionData) + rData := i.featureRemote.DataCopy(model.FunctionTypeIncentiveTableDescriptionData) if rData == nil { return nil, ErrDataNotAvailable } @@ -124,7 +124,7 @@ func (i *IncentiveTable) GetDescriptionsForScope(scope model.ScopeTypeType) ([]m // return list of constraints func (i *IncentiveTable) GetConstraints() ([]model.IncentiveTableConstraintsType, error) { - rData := i.featureRemote.Data(model.FunctionTypeIncentiveTableConstraintsData) + rData := i.featureRemote.DataCopy(model.FunctionTypeIncentiveTableConstraintsData) if rData == nil { return nil, ErrDataNotAvailable } diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 58f9394d..ee6ce0e1 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -42,7 +42,7 @@ func (l *LoadControl) RequestLimitValues() (*model.MsgCounterType, error) { // returns the load control limit descriptions // returns an error if no description data is available yet func (l *LoadControl) GetLimitDescriptions() ([]model.LoadControlLimitDescriptionDataType, error) { - rData := l.featureRemote.Data(model.FunctionTypeLoadControlLimitDescriptionListData) + rData := l.featureRemote.DataCopy(model.FunctionTypeLoadControlLimitDescriptionListData) if rData == nil { return nil, ErrMetadataNotAvailable } @@ -119,7 +119,7 @@ func (l *LoadControl) WriteLimitValues(data []model.LoadControlLimitDataType) (* // return limit data func (l *LoadControl) GetLimitValues() ([]model.LoadControlLimitDataType, error) { - rData := l.featureRemote.Data(model.FunctionTypeLoadControlLimitListData) + rData := l.featureRemote.DataCopy(model.FunctionTypeLoadControlLimitListData) if rData == nil { return nil, ErrDataNotAvailable } diff --git a/features/measurement.go b/features/measurement.go index deb69e0c..0e3abbda 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -42,7 +42,7 @@ func (m *Measurement) RequestValues() (*model.MsgCounterType, error) { // return list of descriptions func (m *Measurement) GetDescriptions() ([]model.MeasurementDescriptionDataType, error) { - rData := m.featureRemote.Data(model.FunctionTypeMeasurementDescriptionListData) + rData := m.featureRemote.DataCopy(model.FunctionTypeMeasurementDescriptionListData) if rData == nil { return nil, ErrMetadataNotAvailable } @@ -96,7 +96,7 @@ func (m *Measurement) GetDescriptionForMeasurementId(measurementId model.Measure // return current values for measurements func (m *Measurement) GetValues() ([]model.MeasurementDataType, error) { - rData := m.featureRemote.Data(model.FunctionTypeMeasurementListData) + rData := m.featureRemote.DataCopy(model.FunctionTypeMeasurementListData) if rData == nil { return nil, ErrDataNotAvailable } @@ -144,7 +144,7 @@ func (m *Measurement) GetValuesForTypeCommodityScope(measurement model.Measureme // return measurement constraints func (m *Measurement) GetConstraints() ([]model.MeasurementConstraintsDataType, error) { - rData := m.featureRemote.Data(model.FunctionTypeMeasurementConstraintsListData) + rData := m.featureRemote.DataCopy(model.FunctionTypeMeasurementConstraintsListData) if rData == nil { return nil, ErrMetadataNotAvailable } diff --git a/features/timeseries.go b/features/timeseries.go index 88ec13c1..f4dced2e 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -57,7 +57,7 @@ func (t *TimeSeries) WriteValues(data []model.TimeSeriesDataType) (*model.MsgCou // return current values for Time Series func (t *TimeSeries) GetValues() ([]model.TimeSeriesDataType, error) { - rData := t.featureRemote.Data(model.FunctionTypeTimeSeriesListData) + rData := t.featureRemote.DataCopy(model.FunctionTypeTimeSeriesListData) if rData == nil { return nil, ErrDataNotAvailable } @@ -100,7 +100,7 @@ func (t *TimeSeries) GetValueForType(timeSeriesType model.TimeSeriesTypeType) (* // return list of descriptions func (t *TimeSeries) GetDescriptions() ([]model.TimeSeriesDescriptionDataType, error) { - rData := t.featureRemote.Data(model.FunctionTypeTimeSeriesDescriptionListData) + rData := t.featureRemote.DataCopy(model.FunctionTypeTimeSeriesDescriptionListData) if rData == nil { return nil, ErrDataNotAvailable } @@ -145,7 +145,7 @@ func (t *TimeSeries) GetDescriptionForType(timeSeriesType model.TimeSeriesTypeTy // return current constraints for Time Series func (t *TimeSeries) GetConstraints() ([]model.TimeSeriesConstraintsDataType, error) { - rData := t.featureRemote.Data(model.FunctionTypeTimeSeriesConstraintsListData) + rData := t.featureRemote.DataCopy(model.FunctionTypeTimeSeriesConstraintsListData) switch constraintsData := rData.(type) { case *model.TimeSeriesConstraintsListDataType: if constraintsData == nil { diff --git a/integration_tests/electricalconnection_test.go b/integration_tests/electricalconnection_test.go index ced18fe9..27ffb415 100644 --- a/integration_tests/electricalconnection_test.go +++ b/integration_tests/electricalconnection_test.go @@ -56,7 +56,7 @@ func (s *ElectricalConnectionSuite) TestDescriptionListData_RecvReply() { model.RoleTypeServer) assert.NotNil(s.T(), ecFeature) - fdata := ecFeature.Data(model.FunctionTypeElectricalConnectionDescriptionListData) + fdata := ecFeature.DataCopy(model.FunctionTypeElectricalConnectionDescriptionListData) if !assert.NotNil(s.T(), fdata) { return } @@ -88,7 +88,7 @@ func (s *ElectricalConnectionSuite) TestParameterDescriptionListData_RecvReply() model.RoleTypeServer) assert.NotNil(s.T(), ecFeature) - fdata := ecFeature.Data(model.FunctionTypeElectricalConnectionParameterDescriptionListData) + fdata := ecFeature.DataCopy(model.FunctionTypeElectricalConnectionParameterDescriptionListData) if !assert.NotNil(s.T(), fdata) { return } @@ -124,7 +124,7 @@ func (s *ElectricalConnectionSuite) TestPermittedValueSetListData_RecvNotifyPart model.RoleTypeServer) assert.NotNil(s.T(), ecFeature) - fdata := ecFeature.Data(model.FunctionTypeElectricalConnectionPermittedValueSetListData) + fdata := ecFeature.DataCopy(model.FunctionTypeElectricalConnectionPermittedValueSetListData) if !assert.NotNil(s.T(), fdata) { return } diff --git a/integration_tests/measurement_test.go b/integration_tests/measurement_test.go index 433e3a46..aa143d07 100644 --- a/integration_tests/measurement_test.go +++ b/integration_tests/measurement_test.go @@ -56,7 +56,7 @@ func (s *MeasurementSuite) TestDescriptionList_Recv() { model.RoleTypeServer) assert.NotNil(s.T(), mFeature) - fdata := mFeature.Data(model.FunctionTypeMeasurementDescriptionListData) + fdata := mFeature.DataCopy(model.FunctionTypeMeasurementDescriptionListData) if !assert.NotNil(s.T(), fdata) { return } @@ -89,7 +89,7 @@ func (s *MeasurementSuite) TestMeasurementList_Recv() { model.RoleTypeServer) assert.NotNil(s.T(), mFeature) - fdata := mFeature.Data(model.FunctionTypeMeasurementListData) + fdata := mFeature.DataCopy(model.FunctionTypeMeasurementListData) if !assert.NotNil(s.T(), fdata) { return } @@ -130,7 +130,7 @@ func (s *MeasurementSuite) TestMeasurementByScope_Recv() { model.RoleTypeServer) assert.NotNil(s.T(), mFeature) - fdata := mFeature.Data(model.FunctionTypeMeasurementDescriptionListData) + fdata := mFeature.DataCopy(model.FunctionTypeMeasurementDescriptionListData) if !assert.NotNil(s.T(), fdata) { return } @@ -140,7 +140,7 @@ func (s *MeasurementSuite) TestMeasurementByScope_Recv() { return } - fdata = mFeature.Data(model.FunctionTypeMeasurementListData) + fdata = mFeature.DataCopy(model.FunctionTypeMeasurementListData) if !assert.NotNil(s.T(), fdata) { return } diff --git a/spine/api.go b/spine/api.go index e6524536..277bcb72 100644 --- a/spine/api.go +++ b/spine/api.go @@ -24,6 +24,20 @@ type Feature interface { Address() *model.FeatureAddressType Type() model.FeatureTypeType Role() model.RoleType + Operations() map[model.FunctionType]*Operations +} + +type FeatureRemote interface { + Feature + DataCopy(function model.FunctionType) any + SetData(function model.FunctionType, data any) + UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType) + Sender() Sender + Device() *DeviceRemoteImpl + Entity() *EntityRemoteImpl + SetOperations(functions []model.FunctionPropertyType) + SetMaxResponseDelay(delay *model.MaxResponseDelayType) + MaxResponseDelayDuration() time.Duration } type FeatureLocal interface { @@ -38,7 +52,7 @@ type FeatureLocal interface { function model.FunctionType, selector any, elements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) + destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) RequestDataBySenderAddress( cmd model.CmdType, sender Sender, @@ -47,12 +61,12 @@ type FeatureLocal interface { maxDelay time.Duration) (*model.MsgCounterType, *model.ErrorType) FetchRequestData( msgCounter model.MsgCounterType, - destination *FeatureRemoteImpl) (any, *model.ErrorType) + destination FeatureRemote) (any, *model.ErrorType) RequestAndFetchData( function model.FunctionType, selector any, elements any, - destination *FeatureRemoteImpl) (any, *model.ErrorType) + destination FeatureRemote) (any, *model.ErrorType) Subscribe(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) // SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed RemoveSubscription(remoteAddress *model.FeatureAddressType) @@ -66,12 +80,12 @@ type FeatureLocal interface { deleteSelector, partialSelector any, partialWithoutSelector bool, deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) + destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) WriteData( function model.FunctionType, deleteSelector, partialSelector any, deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) + destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) HandleMessage(message *Message) *model.ErrorType } diff --git a/spine/binding_manager.go b/spine/binding_manager.go index f3c0c4c4..0a11600d 100644 --- a/spine/binding_manager.go +++ b/spine/binding_manager.go @@ -15,7 +15,7 @@ import ( type BindingEntry struct { id uint64 serverFeature FeatureLocal - clientFeature *FeatureRemoteImpl + clientFeature FeatureRemote } type BindingManagerImpl struct { @@ -168,7 +168,7 @@ func (c *BindingManagerImpl) RemoveBindingsForEntity(remoteEntity *EntityRemoteI continue } - clientFeature := remoteEntity.Feature(item.clientFeature.address.Feature) + clientFeature := remoteEntity.Feature(item.clientFeature.Address().Feature) payload := EventPayload{ Ski: remoteEntity.Device().ski, EventType: EventTypeBindingChange, diff --git a/spine/device_remote.go b/spine/device_remote.go index 16003b0f..a867cbb3 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -90,7 +90,7 @@ func (d *DeviceRemoteImpl) Entities() []*EntityRemoteImpl { } // Return the feature for a given address -func (d *DeviceRemoteImpl) FeatureByAddress(address *model.FeatureAddressType) *FeatureRemoteImpl { +func (d *DeviceRemoteImpl) FeatureByAddress(address *model.FeatureAddressType) FeatureRemote { entity := d.Entity(address.Entity) if entity != nil { return entity.Feature(address.Feature) @@ -120,7 +120,7 @@ func (d *DeviceRemoteImpl) RemoveByAddress(addr []model.AddressEntityType) *Enti } // Get the feature for a given entity, feature type and feature role -func (r *DeviceRemoteImpl) FeatureByEntityTypeAndRole(entity *EntityRemoteImpl, featureType model.FeatureTypeType, role model.RoleType) *FeatureRemoteImpl { +func (r *DeviceRemoteImpl) FeatureByEntityTypeAndRole(entity *EntityRemoteImpl, featureType model.FeatureTypeType, role model.RoleType) FeatureRemote { if len(r.entities) < 1 { return nil } @@ -177,7 +177,7 @@ func (d *DeviceRemoteImpl) AddEntityAndFeatures(initialData bool, data *model.No for _, fi := range data.FeatureInformation { if reflect.DeepEqual(fi.Description.FeatureAddress.Entity, entityAddress) { - if f := unmarshalFeature(entity, fi); f != nil { + if f, ok := unmarshalFeature(entity, fi); ok { entity.AddFeature(f) } } @@ -247,7 +247,7 @@ func (d *DeviceRemoteImpl) VerifyUseCaseScenariosAndFeaturesSupport( nodemgmt := d.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) - usecases := nodemgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + usecases := nodemgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) if usecases == nil || len(usecases.UseCaseInformation) == 0 { return false @@ -321,17 +321,20 @@ func (d *DeviceRemoteImpl) VerifyUseCaseScenariosAndFeaturesSupport( func unmarshalFeature(entity *EntityRemoteImpl, featureData model.NodeManagementDetailedDiscoveryFeatureInformationType, -) *FeatureRemoteImpl { +) (FeatureRemote, bool) { var result *FeatureRemoteImpl - if fid := featureData.Description; fid != nil { + fid := featureData.Description - result = NewFeatureRemoteImpl(uint(*fid.FeatureAddress.Feature), entity, *fid.FeatureType, *fid.Role) - - result.SetDescription(fid.Description) - result.SetMaxResponseDelay(fid.MaxResponseDelay) - result.SetOperations(fid.SupportedFunction) + if fid == nil { + return nil, false } - return result + result = NewFeatureRemoteImpl(uint(*fid.FeatureAddress.Feature), entity, *fid.FeatureType, *fid.Role) + + result.SetDescription(fid.Description) + result.SetMaxResponseDelay(fid.MaxResponseDelay) + result.SetOperations(fid.SupportedFunction) + + return result, true } diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index d7eb535f..874ebc40 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -118,7 +118,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { } nodeMgmt.UpdateData(model.FunctionTypeNodeManagementUseCaseData, newData, nil, nil) - data := nodeMgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) address := model.FeatureAddressType{ Device: s.remoteDevice.address, @@ -135,7 +135,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { []model.UseCaseScenarioSupportType{1}, ) nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) - data = nodeMgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + data = nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeEVSE, @@ -155,7 +155,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { []model.UseCaseScenarioSupportType{1}, ) nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) - data = nodeMgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + data = nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeEVSE, @@ -175,7 +175,7 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { []model.UseCaseScenarioSupportType{1}, ) nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) - data = nodeMgmt.Data(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + data = nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeEVSE, diff --git a/spine/entity_remote.go b/spine/entity_remote.go index ed3b1e0f..8fe23334 100644 --- a/spine/entity_remote.go +++ b/spine/entity_remote.go @@ -7,7 +7,7 @@ import ( type EntityRemoteImpl struct { *EntityImpl device *DeviceRemoteImpl - features []*FeatureRemoteImpl + features []FeatureRemote } func NewEntityRemoteImpl(device *DeviceRemoteImpl, eType model.EntityTypeType, entityAddress []model.AddressEntityType) *EntityRemoteImpl { @@ -21,15 +21,15 @@ func (r *EntityRemoteImpl) Device() *DeviceRemoteImpl { return r.device } -func (r *EntityRemoteImpl) AddFeature(f *FeatureRemoteImpl) { +func (r *EntityRemoteImpl) AddFeature(f FeatureRemote) { r.features = append(r.features, f) } -func (r *EntityRemoteImpl) Features() []*FeatureRemoteImpl { +func (r *EntityRemoteImpl) Features() []FeatureRemote { return r.features } -func (r *EntityRemoteImpl) Feature(addressFeature *model.AddressFeatureType) *FeatureRemoteImpl { +func (r *EntityRemoteImpl) Feature(addressFeature *model.AddressFeatureType) FeatureRemote { for _, f := range r.features { if *f.Address().Feature == *addressFeature { return f diff --git a/spine/events.go b/spine/events.go index ab1de0ef..2e7ca54f 100644 --- a/spine/events.go +++ b/spine/events.go @@ -39,7 +39,7 @@ type EventPayload struct { ChangeType ElementChangeType // required Device *DeviceRemoteImpl // required for DetailedDiscovery Call Entity *EntityRemoteImpl // required for DetailedDiscovery Call and Notify - Feature *FeatureRemoteImpl + Feature FeatureRemote CmdClassifier *model.CmdClassifierType // optional, used together with EventType EventTypeDataChange Data any } diff --git a/spine/feature_local.go b/spine/feature_local.go index 84669ddd..e9ed6d3e 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -140,7 +140,7 @@ func (r *FeatureLocalImpl) RequestData( function model.FunctionType, selector any, elements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) { + destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) { fd := r.functionData(function) cmd := fd.ReadCmdType(selector, elements) @@ -167,7 +167,7 @@ func (r *FeatureLocalImpl) RequestDataBySenderAddress( // this will block until the response is received func (r *FeatureLocalImpl) FetchRequestData( msgCounter model.MsgCounterType, - destination *FeatureRemoteImpl) (any, *model.ErrorType) { + destination FeatureRemote) (any, *model.ErrorType) { return r.pendingRequests.GetData(destination.Device().ski, msgCounter) } @@ -178,7 +178,7 @@ func (r *FeatureLocalImpl) RequestAndFetchData( function model.FunctionType, selector any, elements any, - destination *FeatureRemoteImpl) (any, *model.ErrorType) { + destination FeatureRemote) (any, *model.ErrorType) { msgCounter, err := r.RequestData(function, selector, elements, destination) if err != nil { @@ -367,7 +367,7 @@ func (r *FeatureLocalImpl) NotifyData( deleteSelector, partialSelector any, partialWithoutSelector bool, deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) { + destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) { fd := r.functionData(function) cmd := fd.NotifyCmdType(deleteSelector, partialSelector, partialWithoutSelector, deleteElements) @@ -383,7 +383,7 @@ func (r *FeatureLocalImpl) WriteData( function model.FunctionType, deleteSelector, partialSelector any, deleteElements any, - destination *FeatureRemoteImpl) (*model.MsgCounterType, *model.ErrorType) { + destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) { fd := r.functionData(function) cmd := fd.WriteCmdType(deleteSelector, partialSelector, deleteElements) @@ -473,7 +473,7 @@ func (r *FeatureLocalImpl) processResult(message *Message) *model.ErrorType { } } -func (r *FeatureLocalImpl) processRead(function model.FunctionType, requestHeader *model.HeaderType, featureRemote *FeatureRemoteImpl) *model.ErrorType { +func (r *FeatureLocalImpl) processRead(function model.FunctionType, requestHeader *model.HeaderType, featureRemote FeatureRemote) *model.ErrorType { // is this a read request to a local server/special feature? if r.role == model.RoleTypeClient { // Read requests to a client feature are not allowed @@ -488,7 +488,7 @@ func (r *FeatureLocalImpl) processRead(function model.FunctionType, requestHeade return nil } -func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, requestHeader *model.HeaderType, featureRemote *FeatureRemoteImpl) *model.ErrorType { +func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, requestHeader *model.HeaderType, featureRemote FeatureRemote) *model.ErrorType { featureRemote.UpdateData(function, data, filterPartial, filterDelete) _ = r.pendingRequests.SetData(featureRemote.Device().ski, *requestHeader.MsgCounterReference, data) // an error in SetData only means that there is no pendingRequest waiting for this dataset @@ -510,7 +510,7 @@ func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, f return nil } -func (r *FeatureLocalImpl) processNotify(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, featureRemote *FeatureRemoteImpl) *model.ErrorType { +func (r *FeatureLocalImpl) processNotify(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, featureRemote FeatureRemote) *model.ErrorType { featureRemote.UpdateData(function, data, filterPartial, filterDelete) payload := EventPayload{ diff --git a/spine/feature_local_test.go b/spine/feature_local_test.go index ea335866..a2323963 100644 --- a/spine/feature_local_test.go +++ b/spine/feature_local_test.go @@ -65,7 +65,7 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Rep // set response msgErr := suite.sut.HandleMessage(&replyMsg) if assert.Nil(suite.T(), msgErr) { - remoteData := suite.remoteFeature.Data(suite.function) + remoteData := suite.remoteFeature.DataCopy(suite.function) assert.IsType(suite.T(), &model.DeviceClassificationManufacturerDataType{}, remoteData, "Data has wrong type") } @@ -113,7 +113,7 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Err // set response msgErr := suite.sut.HandleMessage(&replyMsg) if assert.Nil(suite.T(), msgErr) { - remoteData := suite.remoteFeature.Data(suite.function) + remoteData := suite.remoteFeature.DataCopy(suite.function) assert.Nil(suite.T(), remoteData) } diff --git a/spine/feature_remote.go b/spine/feature_remote.go index 46448a2c..6ae964e3 100644 --- a/spine/feature_remote.go +++ b/spine/feature_remote.go @@ -13,8 +13,11 @@ import ( const defaultMaxResponseDelay = time.Duration(time.Second * 10) +var _ FeatureRemote = (*FeatureRemoteImpl)(nil) + type FeatureRemoteImpl struct { *FeatureImpl + entity *EntityRemoteImpl functionDataMap map[model.FunctionType]FunctionData maxResponseDelay *time.Duration @@ -40,7 +43,7 @@ func NewFeatureRemoteImpl(id uint, entity *EntityRemoteImpl, ftype model.Feature return res } -func (r *FeatureRemoteImpl) Data(function model.FunctionType) any { +func (r *FeatureRemoteImpl) DataCopy(function model.FunctionType) any { r.mux.Lock() defer r.mux.Unlock() diff --git a/spine/message.go b/spine/message.go index 86d6a5b4..1e49ad58 100644 --- a/spine/message.go +++ b/spine/message.go @@ -8,7 +8,7 @@ type Message struct { Cmd model.CmdType FilterPartial *model.FilterType FilterDelete *model.FilterType - FeatureRemote *FeatureRemoteImpl + FeatureRemote FeatureRemote EntityRemote *EntityRemoteImpl DeviceRemote *DeviceRemoteImpl } @@ -17,7 +17,7 @@ type ResultMessage struct { MsgCounterReference model.MsgCounterType // required Result *model.ResultDataType // required, may not be nil FeatureLocal *FeatureLocalImpl // required, may not be nil - FeatureRemote *FeatureRemoteImpl // required, may not be nil + FeatureRemote FeatureRemote // required, may not be nil EntityRemote *EntityRemoteImpl // required, may not be nil DeviceRemote *DeviceRemoteImpl // required, may not be nil } diff --git a/spine/nodemanagement_destinationlist.go b/spine/nodemanagement_destinationlist.go index 098a5e49..4cad3983 100644 --- a/spine/nodemanagement_destinationlist.go +++ b/spine/nodemanagement_destinationlist.go @@ -11,7 +11,7 @@ func (r *NodeManagementImpl) RequestDestinationListData(remoteDeviceAddress *mod return nil, model.NewErrorTypeFromString("Not implemented") } -func (r *NodeManagementImpl) processReadDestinationListData(featureRemote *FeatureRemoteImpl, requestHeader *model.HeaderType) error { +func (r *NodeManagementImpl) processReadDestinationListData(featureRemote FeatureRemote, requestHeader *model.HeaderType) error { data := []model.NodeManagementDestinationDataType{ r.Device().DestinationData(), } diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go index 626c6641..b89bcf06 100644 --- a/spine/nodemanagement_usecase.go +++ b/spine/nodemanagement_usecase.go @@ -28,7 +28,7 @@ func (r *NodeManagementImpl) NotifyUseCaseData(remoteDevice *DeviceRemoteImpl) ( return featureRemote.Sender().Notify(r.Address(), rfAdress, cmd) } -func (r *NodeManagementImpl) processReadUseCaseData(featureRemote *FeatureRemoteImpl, requestHeader *model.HeaderType) error { +func (r *NodeManagementImpl) processReadUseCaseData(featureRemote FeatureRemote, requestHeader *model.HeaderType) error { cmd := r.functionData(model.FunctionTypeNodeManagementUseCaseData).ReplyCmdType(false) return featureRemote.Sender().Reply(requestHeader, r.Address(), cmd) diff --git a/spine/subscription_manager.go b/spine/subscription_manager.go index d05ae14b..88bbd895 100644 --- a/spine/subscription_manager.go +++ b/spine/subscription_manager.go @@ -15,7 +15,7 @@ import ( type SubscriptionEntry struct { id uint64 serverFeature FeatureLocal - clientFeature *FeatureRemoteImpl + clientFeature FeatureRemote } type SubscriptionManagerImpl struct { @@ -169,7 +169,7 @@ func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity *Ent continue } - clientFeature := remoteEntity.Feature(item.clientFeature.address.Feature) + clientFeature := remoteEntity.Feature(item.clientFeature.Address().Feature) payload := EventPayload{ Ski: remoteEntity.Device().ski, EventType: EventTypeSubscriptionChange, From fde3bdf754c2d727a19a1cfe6861251a7404b3c5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 18:55:38 +0100 Subject: [PATCH 142/240] Introduce EntityLocal and EntityRemote interfaces --- features/deviceclassification.go | 2 +- features/deviceclassification_test.go | 4 +- features/deviceconfiguration.go | 2 +- features/deviceconfiguration_test.go | 4 +- features/devicediagnosis.go | 2 +- features/devicediagnosis_test.go | 4 +- features/electricalconnection.go | 2 +- features/electricalconnection_test.go | 4 +- features/feature.go | 6 +-- features/helper_test.go | 2 +- features/identification.go | 2 +- features/identification_test.go | 4 +- features/incentivetable.go | 2 +- features/incentivetable_test.go | 4 +- features/loadcontrol.go | 2 +- features/loadcontrol_test.go | 4 +- features/measurement.go | 2 +- features/measurement_test.go | 4 +- features/timeseries.go | 2 +- features/timeseries_test.go | 4 +- .../emobility_measurement_test.go | 2 +- spine/api.go | 51 +++++++++++++++++-- spine/binding_manager.go | 2 +- spine/device_local.go | 18 +++---- spine/device_remote.go | 22 ++++---- spine/device_remote_test.go | 6 +-- spine/entity.go | 2 + spine/entity_local.go | 2 + spine/entity_remote.go | 2 + spine/events.go | 2 +- spine/feature_local.go | 14 ++--- spine/feature_remote.go | 6 +-- spine/message.go | 6 +-- spine/nodemanagement.go | 4 +- spine/subscription_manager.go | 2 +- 35 files changed, 127 insertions(+), 76 deletions(-) diff --git a/features/deviceclassification.go b/features/deviceclassification.go index 4292ef6e..7ff22816 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -9,7 +9,7 @@ type DeviceClassification struct { *FeatureImpl } -func NewDeviceClassification(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceClassification, error) { +func NewDeviceClassification(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*DeviceClassification, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/deviceclassification_test.go b/features/deviceclassification_test.go index f129c716..0a910b2f 100644 --- a/features/deviceclassification_test.go +++ b/features/deviceclassification_test.go @@ -18,8 +18,8 @@ func TestDeviceClassificationSuite(t *testing.T) { type DeviceClassificationSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote deviceClassification *features.DeviceClassification sentMessage []byte diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index 8fcc203d..4fee3a66 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -9,7 +9,7 @@ type DeviceConfiguration struct { *FeatureImpl } -func NewDeviceConfiguration(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceConfiguration, error) { +func NewDeviceConfiguration(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*DeviceConfiguration, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index 45af5b5f..a91a3a1a 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -18,8 +18,8 @@ func TestDeviceConfigurationSuite(t *testing.T) { type DeviceConfigurationSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote deviceConfiguration *features.DeviceConfiguration sentMessage []byte diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index 7ce673ef..92a8032c 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -9,7 +9,7 @@ type DeviceDiagnosis struct { *FeatureImpl } -func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceDiagnosis, error) { +func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*DeviceDiagnosis, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 103175f1..101ac05d 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -18,8 +18,8 @@ func TestDeviceDiagnosisSuite(t *testing.T) { type DeviceDiagnosisSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote deviceDiagnosis *features.DeviceDiagnosis sentMessage []byte diff --git a/features/electricalconnection.go b/features/electricalconnection.go index 72f0e17f..a8afedf7 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -9,7 +9,7 @@ type ElectricalConnection struct { *FeatureImpl } -func NewElectricalConnection(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*ElectricalConnection, error) { +func NewElectricalConnection(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*ElectricalConnection, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 91add2a4..af8a6fa9 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -18,8 +18,8 @@ func TestElectricalConnectionSuite(t *testing.T) { type ElectricalConnectionSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote electricalConnection *features.ElectricalConnection sentMessage []byte diff --git a/features/feature.go b/features/feature.go index edb06d11..d12b2dda 100644 --- a/features/feature.go +++ b/features/feature.go @@ -19,18 +19,18 @@ type FeatureImpl struct { remoteRole model.RoleType spineLocalDevice *spine.DeviceLocalImpl - localEntity *spine.EntityLocalImpl + localEntity spine.EntityLocal featureLocal spine.FeatureLocal featureRemote spine.FeatureRemote remoteDevice *spine.DeviceRemoteImpl - remoteEntity *spine.EntityRemoteImpl + remoteEntity spine.EntityRemote } var _ Feature = (*FeatureImpl)(nil) -func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*FeatureImpl, error) { +func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*FeatureImpl, error) { f := &FeatureImpl{ featureType: featureType, localRole: localRole, diff --git a/features/helper_test.go b/features/helper_test.go index ac2ecc71..8103cd93 100644 --- a/features/helper_test.go +++ b/features/helper_test.go @@ -14,7 +14,7 @@ type featureFunctions struct { functions []model.FunctionType } -func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, featureFunctions []featureFunctions) (*spine.EntityLocalImpl, *spine.EntityRemoteImpl) { +func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, featureFunctions []featureFunctions) (spine.EntityLocal, spine.EntityRemote) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) diff --git a/features/identification.go b/features/identification.go index ccf840ab..8200460e 100644 --- a/features/identification.go +++ b/features/identification.go @@ -9,7 +9,7 @@ type Identification struct { *FeatureImpl } -func NewIdentification(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*Identification, error) { +func NewIdentification(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*Identification, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeIdentification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/identification_test.go b/features/identification_test.go index 4f60a646..85a93fd7 100644 --- a/features/identification_test.go +++ b/features/identification_test.go @@ -18,8 +18,8 @@ func TestIdentificationSuite(t *testing.T) { type IdentificationSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote identification *features.Identification sentMessage []byte diff --git a/features/incentivetable.go b/features/incentivetable.go index b47b2764..fe65ec3e 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -9,7 +9,7 @@ type IncentiveTable struct { *FeatureImpl } -func NewIncentiveTable(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*IncentiveTable, error) { +func NewIncentiveTable(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*IncentiveTable, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 707bf80e..26c26d2c 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -18,8 +18,8 @@ func TestIncentiveTableSuite(t *testing.T) { type IncentiveTableSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote incentiveTable *features.IncentiveTable sentMessage []byte diff --git a/features/loadcontrol.go b/features/loadcontrol.go index ee6ce0e1..ed732920 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -9,7 +9,7 @@ type LoadControl struct { *FeatureImpl } -func NewLoadControl(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*LoadControl, error) { +func NewLoadControl(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*LoadControl, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeLoadControl, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index c27d80fa..09c7ba4e 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -18,8 +18,8 @@ func TestLoadControlSuite(t *testing.T) { type LoadControlSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote loadControl *features.LoadControl sentMessage []byte diff --git a/features/measurement.go b/features/measurement.go index 0e3abbda..a8ecc023 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -9,7 +9,7 @@ type Measurement struct { *FeatureImpl } -func NewMeasurement(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*Measurement, error) { +func NewMeasurement(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*Measurement, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeMeasurement, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/measurement_test.go b/features/measurement_test.go index e10ed568..c02a3583 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -19,8 +19,8 @@ func TestMeasurementSuite(t *testing.T) { type MeasurementSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote measurement *features.Measurement sentMessage []byte diff --git a/features/timeseries.go b/features/timeseries.go index f4dced2e..e490c81a 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -9,7 +9,7 @@ type TimeSeries struct { *FeatureImpl } -func NewTimeSeries(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*TimeSeries, error) { +func NewTimeSeries(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*TimeSeries, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeTimeSeries, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/timeseries_test.go b/features/timeseries_test.go index 0b769380..bedf9e37 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -19,8 +19,8 @@ func TestTimeSeriesSuite(t *testing.T) { type TimeSeriesSuite struct { suite.Suite - localEntity *spine.EntityLocalImpl - remoteEntity *spine.EntityRemoteImpl + localEntity spine.EntityLocal + remoteEntity spine.EntityRemote timeSeries *features.TimeSeries sentMessage []byte diff --git a/integration_tests/emobility_measurement_test.go b/integration_tests/emobility_measurement_test.go index fc29d7ff..346b40b6 100644 --- a/integration_tests/emobility_measurement_test.go +++ b/integration_tests/emobility_measurement_test.go @@ -20,7 +20,7 @@ type EmobilityMeasurementSuite struct { spine.SpineDataConnection sut *spine.DeviceLocalImpl - localEntity *spine.EntityLocalImpl + localEntity spine.EntityLocal measurement *features.Measurement electricalconnection *features.ElectricalConnection diff --git a/spine/api.go b/spine/api.go index 277bcb72..0a7216ad 100644 --- a/spine/api.go +++ b/spine/api.go @@ -18,6 +18,51 @@ type DeviceLocalConnection interface { AddRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing } +/* Entity */ + +type Entity interface { + EntityType() model.EntityTypeType + Address() *model.EntityAddressType + Description() *model.DescriptionType + SetDescription(d *model.DescriptionType) + NextFeatureId() uint +} + +type EntityLocal interface { + Entity + Device() *DeviceLocalImpl + AddFeature(f FeatureLocal) + GetOrAddFeature(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal + FeatureOfTypeAndRole(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal + Features() []FeatureLocal + Feature(addressFeature *model.AddressFeatureType) FeatureLocal + Information() *model.NodeManagementDetailedDiscoveryEntityInformationType + AddUseCaseSupport( + actor model.UseCaseActorType, + useCaseName model.UseCaseNameType, + useCaseVersion model.SpecificationVersionType, + useCaseDocumemtSubRevision string, + useCaseAvailable bool, + scenarios []model.UseCaseScenarioSupportType, + ) + RemoveUseCaseSupport( + actor model.UseCaseActorType, + useCaseName model.UseCaseNameType, + ) + RemoveAllUseCaseSupports() + RemoveAllSubscriptions() + RemoveAllBindings() +} + +type EntityRemote interface { + Entity + Device() *DeviceRemoteImpl + AddFeature(f FeatureRemote) + Features() []FeatureRemote + Feature(addressFeature *model.AddressFeatureType) FeatureRemote + RemoveAllFeatures() +} + /* Feature */ type Feature interface { @@ -34,7 +79,7 @@ type FeatureRemote interface { UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType) Sender() Sender Device() *DeviceRemoteImpl - Entity() *EntityRemoteImpl + Entity() EntityRemote SetOperations(functions []model.FunctionPropertyType) SetMaxResponseDelay(delay *model.MaxResponseDelayType) MaxResponseDelayDuration() time.Duration @@ -160,7 +205,7 @@ type BindingManager interface { AddBinding(remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error RemoveBindingsForDevice(remoteDevice *DeviceRemoteImpl) - RemoveBindingsForEntity(remoteEntity *EntityRemoteImpl) + RemoveBindingsForEntity(remoteEntity EntityRemote) Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry } @@ -171,7 +216,7 @@ type SubscriptionManager interface { AddSubscription(remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error RemoveSubscriptionsForDevice(remoteDevice *DeviceRemoteImpl) - RemoveSubscriptionsForEntity(remoteEntity *EntityRemoteImpl) + RemoveSubscriptionsForEntity(remoteEntity EntityRemote) Subscriptions(remoteDevice *DeviceRemoteImpl) []*SubscriptionEntry SubscriptionsOnFeature(featureAddress model.FeatureAddressType) []*SubscriptionEntry } diff --git a/spine/binding_manager.go b/spine/binding_manager.go index 0a11600d..b46cad76 100644 --- a/spine/binding_manager.go +++ b/spine/binding_manager.go @@ -153,7 +153,7 @@ func (c *BindingManagerImpl) RemoveBindingsForDevice(remoteDevice *DeviceRemoteI } // Remove all existing bindings for a given remote device entity -func (c *BindingManagerImpl) RemoveBindingsForEntity(remoteEntity *EntityRemoteImpl) { +func (c *BindingManagerImpl) RemoveBindingsForEntity(remoteEntity EntityRemote) { if remoteEntity == nil { return } diff --git a/spine/device_local.go b/spine/device_local.go index 65b57e07..1900c595 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -14,7 +14,7 @@ import ( type DeviceLocalImpl struct { *DeviceImpl - entities []*EntityLocalImpl + entities []EntityLocal subscriptionManager SubscriptionManager bindingManager BindingManager heartbeatManager HeartbeatManager @@ -271,7 +271,7 @@ func (r *DeviceLocalImpl) HeartbeatManager() HeartbeatManager { return r.heartbeatManager } -func (r *DeviceLocalImpl) AddEntity(entity *EntityLocalImpl) { +func (r *DeviceLocalImpl) AddEntity(entity EntityLocal) { r.mux.Lock() defer r.mux.Unlock() @@ -280,7 +280,7 @@ func (r *DeviceLocalImpl) AddEntity(entity *EntityLocalImpl) { r.notifySubscribersOfEntity(entity, model.NetworkManagementStateChangeTypeAdded) } -func (r *DeviceLocalImpl) RemoveEntity(entity *EntityLocalImpl) { +func (r *DeviceLocalImpl) RemoveEntity(entity EntityLocal) { entity.RemoveAllUseCaseSupports() entity.RemoveAllSubscriptions() entity.RemoveAllBindings() @@ -288,7 +288,7 @@ func (r *DeviceLocalImpl) RemoveEntity(entity *EntityLocalImpl) { r.mux.Lock() defer r.mux.Unlock() - var entities []*EntityLocalImpl + var entities []EntityLocal for _, e := range r.entities { if e != entity { entities = append(entities, e) @@ -298,14 +298,14 @@ func (r *DeviceLocalImpl) RemoveEntity(entity *EntityLocalImpl) { r.entities = entities } -func (r *DeviceLocalImpl) Entities() []*EntityLocalImpl { +func (r *DeviceLocalImpl) Entities() []EntityLocal { r.mux.Lock() defer r.mux.Unlock() return r.entities } -func (r *DeviceLocalImpl) Entity(id []model.AddressEntityType) *EntityLocalImpl { +func (r *DeviceLocalImpl) Entity(id []model.AddressEntityType) EntityLocal { r.mux.Lock() defer r.mux.Unlock() @@ -317,12 +317,12 @@ func (r *DeviceLocalImpl) Entity(id []model.AddressEntityType) *EntityLocalImpl return nil } -func (r *DeviceLocalImpl) EntityForType(entityType model.EntityTypeType) *EntityLocalImpl { +func (r *DeviceLocalImpl) EntityForType(entityType model.EntityTypeType) EntityLocal { r.mux.Lock() defer r.mux.Unlock() for _, e := range r.entities { - if e.eType == entityType { + if e.EntityType() == entityType { return e } } @@ -369,7 +369,7 @@ func (r *DeviceLocalImpl) NotifySubscribers(featureAddress *model.FeatureAddress } } -func (r *DeviceLocalImpl) notifySubscribersOfEntity(entity *EntityLocalImpl, state model.NetworkManagementStateChangeType) { +func (r *DeviceLocalImpl) notifySubscribersOfEntity(entity EntityLocal, state model.NetworkManagementStateChangeType) { deviceInformation := r.Information() entityInformation := *entity.Information() entityInformation.Description.LastStateChange = &state diff --git a/spine/device_remote.go b/spine/device_remote.go index a867cbb3..dde7a529 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -16,7 +16,7 @@ type DeviceRemoteImpl struct { ski string - entities []*EntityRemoteImpl + entities []EntityRemote entitiesMutex sync.Mutex sender Sender @@ -72,7 +72,7 @@ func (d *DeviceRemoteImpl) Sender() Sender { } // Return an entity with a given address -func (d *DeviceRemoteImpl) Entity(id []model.AddressEntityType) *EntityRemoteImpl { +func (d *DeviceRemoteImpl) Entity(id []model.AddressEntityType) EntityRemote { d.entitiesMutex.Lock() defer d.entitiesMutex.Unlock() @@ -85,7 +85,7 @@ func (d *DeviceRemoteImpl) Entity(id []model.AddressEntityType) *EntityRemoteImp } // Return all entities of this device -func (d *DeviceRemoteImpl) Entities() []*EntityRemoteImpl { +func (d *DeviceRemoteImpl) Entities() []EntityRemote { return d.entities } @@ -99,7 +99,7 @@ func (d *DeviceRemoteImpl) FeatureByAddress(address *model.FeatureAddressType) F } // Remove an entity with a given address from this device -func (d *DeviceRemoteImpl) RemoveByAddress(addr []model.AddressEntityType) *EntityRemoteImpl { +func (d *DeviceRemoteImpl) RemoveByAddress(addr []model.AddressEntityType) EntityRemote { entityForRemoval := d.Entity(addr) if entityForRemoval == nil { return nil @@ -108,7 +108,7 @@ func (d *DeviceRemoteImpl) RemoveByAddress(addr []model.AddressEntityType) *Enti d.entitiesMutex.Lock() defer d.entitiesMutex.Unlock() - var newEntities []*EntityRemoteImpl + var newEntities []EntityRemote for _, item := range d.entities { if !reflect.DeepEqual(item, entityForRemoval) { newEntities = append(newEntities, item) @@ -120,7 +120,7 @@ func (d *DeviceRemoteImpl) RemoveByAddress(addr []model.AddressEntityType) *Enti } // Get the feature for a given entity, feature type and feature role -func (r *DeviceRemoteImpl) FeatureByEntityTypeAndRole(entity *EntityRemoteImpl, featureType model.FeatureTypeType, role model.RoleType) FeatureRemote { +func (r *DeviceRemoteImpl) FeatureByEntityTypeAndRole(entity EntityRemote, featureType model.FeatureTypeType, role model.RoleType) FeatureRemote { if len(r.entities) < 1 { return nil } @@ -156,8 +156,8 @@ func (d *DeviceRemoteImpl) UpdateDevice(description *model.NetworkManagementDevi } } -func (d *DeviceRemoteImpl) AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]*EntityRemoteImpl, error) { - rEntites := make([]*EntityRemoteImpl, 0) +func (d *DeviceRemoteImpl) AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]EntityRemote, error) { + rEntites := make([]EntityRemote, 0) for _, ei := range data.EntityInformation { if err := d.CheckEntityInformation(initialData, ei); err != nil { @@ -221,12 +221,12 @@ func (d *DeviceRemoteImpl) CheckEntityInformation(initialData bool, entity model return nil } -func (d *DeviceRemoteImpl) addNewEntity(eType model.EntityTypeType, address []model.AddressEntityType) *EntityRemoteImpl { +func (d *DeviceRemoteImpl) addNewEntity(eType model.EntityTypeType, address []model.AddressEntityType) EntityRemote { newEntity := NewEntityRemoteImpl(d, eType, address) return d.AddEntity(newEntity) } -func (d *DeviceRemoteImpl) AddEntity(entity *EntityRemoteImpl) *EntityRemoteImpl { +func (d *DeviceRemoteImpl) AddEntity(entity EntityRemote) EntityRemote { d.entitiesMutex.Lock() defer d.entitiesMutex.Unlock() @@ -319,7 +319,7 @@ func (d *DeviceRemoteImpl) VerifyUseCaseScenariosAndFeaturesSupport( return entityWithServerFeaturesFound } -func unmarshalFeature(entity *EntityRemoteImpl, +func unmarshalFeature(entity EntityRemote, featureData model.NodeManagementDetailedDiscoveryFeatureInformationType, ) (FeatureRemote, bool) { var result *FeatureRemoteImpl diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index 874ebc40..637113bb 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -23,7 +23,7 @@ type DeviceRemoteSuite struct { localDevice *DeviceLocalImpl remoteDevice *DeviceRemoteImpl - remoteEntity *EntityRemoteImpl + remoteEntity EntityRemote } func (s *DeviceRemoteSuite) WriteSpineMessage([]byte) {} @@ -121,8 +121,8 @@ func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) address := model.FeatureAddressType{ - Device: s.remoteDevice.address, - Entity: s.remoteEntity.address.Entity, + Device: s.remoteDevice.Address(), + Entity: s.remoteEntity.Address().Entity, } data.AddUseCaseSupport( diff --git a/spine/entity.go b/spine/entity.go index b80abf37..8e48b99c 100644 --- a/spine/entity.go +++ b/spine/entity.go @@ -17,6 +17,8 @@ type EntityImpl struct { fIdGenerator func() uint } +var _ Entity = (*EntityImpl)(nil) + func NewEntity(eType model.EntityTypeType, deviceAdress *model.AddressDeviceType, entityAddress []model.AddressEntityType) *EntityImpl { entity := &EntityImpl{ eType: eType, diff --git a/spine/entity_local.go b/spine/entity_local.go index 0c4aacbd..c5a4dfa0 100644 --- a/spine/entity_local.go +++ b/spine/entity_local.go @@ -4,6 +4,8 @@ import ( "github.com/enbility/eebus-go/spine/model" ) +var _ EntityLocal = (*EntityLocalImpl)(nil) + type EntityLocalImpl struct { *EntityImpl device *DeviceLocalImpl diff --git a/spine/entity_remote.go b/spine/entity_remote.go index 8fe23334..c0dbea83 100644 --- a/spine/entity_remote.go +++ b/spine/entity_remote.go @@ -4,6 +4,8 @@ import ( "github.com/enbility/eebus-go/spine/model" ) +var _ EntityRemote = (*EntityRemoteImpl)(nil) + type EntityRemoteImpl struct { *EntityImpl device *DeviceRemoteImpl diff --git a/spine/events.go b/spine/events.go index 2e7ca54f..581125a4 100644 --- a/spine/events.go +++ b/spine/events.go @@ -38,7 +38,7 @@ type EventPayload struct { EventType EventType // required ChangeType ElementChangeType // required Device *DeviceRemoteImpl // required for DetailedDiscovery Call - Entity *EntityRemoteImpl // required for DetailedDiscovery Call and Notify + Entity EntityRemote // required for DetailedDiscovery Call and Notify Feature FeatureRemote CmdClassifier *model.CmdClassifierType // optional, used together with EventType EventTypeDataChange Data any diff --git a/spine/feature_local.go b/spine/feature_local.go index e9ed6d3e..9633da53 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -17,7 +17,7 @@ type FeatureLocalImpl struct { *FeatureImpl muxResultCB sync.Mutex - entity *EntityLocalImpl + entity EntityLocal functionDataMap map[model.FunctionType]FunctionDataCmd pendingRequests PendingRequests resultHandler []FeatureResult @@ -29,7 +29,7 @@ type FeatureLocalImpl struct { mux sync.Mutex } -func NewFeatureLocalImpl(id uint, entity *EntityLocalImpl, ftype model.FeatureTypeType, role model.RoleType) *FeatureLocalImpl { +func NewFeatureLocalImpl(id uint, entity EntityLocal, ftype model.FeatureTypeType, role model.RoleType) *FeatureLocalImpl { res := &FeatureLocalImpl{ FeatureImpl: NewFeatureImpl( featureAddressType(id, entity.Address()), @@ -53,7 +53,7 @@ func (r *FeatureLocalImpl) Device() *DeviceLocalImpl { return r.entity.Device() } -func (r *FeatureLocalImpl) Entity() *EntityLocalImpl { +func (r *FeatureLocalImpl) Entity() EntityLocal { return r.entity } @@ -193,7 +193,7 @@ func (r *FeatureLocalImpl) Subscribe(remoteAddress *model.FeatureAddressType) (* if remoteAddress.Device == nil { return nil, model.NewErrorTypeFromString("device not found") } - remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) + remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) if remoteDevice == nil { return nil, model.NewErrorTypeFromString("device not found") } @@ -216,7 +216,7 @@ func (r *FeatureLocalImpl) Subscribe(remoteAddress *model.FeatureAddressType) (* // Remove a subscriptions to a remote feature func (r *FeatureLocalImpl) RemoveSubscription(remoteAddress *model.FeatureAddressType) { - remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) + remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) if remoteDevice == nil { return } @@ -278,7 +278,7 @@ func (r *FeatureLocalImpl) SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remo // Bind to a remote feature func (r *FeatureLocalImpl) Bind(remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { - remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) + remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) if remoteDevice == nil { return nil, model.NewErrorTypeFromString("device not found") } @@ -301,7 +301,7 @@ func (r *FeatureLocalImpl) Bind(remoteAddress *model.FeatureAddressType) (*model // Remove a binding to a remote feature func (r *FeatureLocalImpl) RemoveBinding(remoteAddress *model.FeatureAddressType) { - remoteDevice := r.entity.device.RemoteDeviceForAddress(*remoteAddress.Device) + remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) if remoteDevice == nil { return } diff --git a/spine/feature_remote.go b/spine/feature_remote.go index 6ae964e3..4fc5e983 100644 --- a/spine/feature_remote.go +++ b/spine/feature_remote.go @@ -18,14 +18,14 @@ var _ FeatureRemote = (*FeatureRemoteImpl)(nil) type FeatureRemoteImpl struct { *FeatureImpl - entity *EntityRemoteImpl + entity EntityRemote functionDataMap map[model.FunctionType]FunctionData maxResponseDelay *time.Duration mux sync.Mutex } -func NewFeatureRemoteImpl(id uint, entity *EntityRemoteImpl, ftype model.FeatureTypeType, role model.RoleType) *FeatureRemoteImpl { +func NewFeatureRemoteImpl(id uint, entity EntityRemote, ftype model.FeatureTypeType, role model.RoleType) *FeatureRemoteImpl { res := &FeatureRemoteImpl{ FeatureImpl: NewFeatureImpl( featureAddressType(id, entity.Address()), @@ -75,7 +75,7 @@ func (r *FeatureRemoteImpl) Device() *DeviceRemoteImpl { return r.entity.Device() } -func (r *FeatureRemoteImpl) Entity() *EntityRemoteImpl { +func (r *FeatureRemoteImpl) Entity() EntityRemote { return r.entity } diff --git a/spine/message.go b/spine/message.go index 1e49ad58..32977fd1 100644 --- a/spine/message.go +++ b/spine/message.go @@ -9,15 +9,15 @@ type Message struct { FilterPartial *model.FilterType FilterDelete *model.FilterType FeatureRemote FeatureRemote - EntityRemote *EntityRemoteImpl + EntityRemote EntityRemote DeviceRemote *DeviceRemoteImpl } type ResultMessage struct { MsgCounterReference model.MsgCounterType // required Result *model.ResultDataType // required, may not be nil - FeatureLocal *FeatureLocalImpl // required, may not be nil + FeatureLocal FeatureLocal // required, may not be nil FeatureRemote FeatureRemote // required, may not be nil - EntityRemote *EntityRemoteImpl // required, may not be nil + EntityRemote EntityRemote // required, may not be nil DeviceRemote *DeviceRemoteImpl // required, may not be nil } diff --git a/spine/nodemanagement.go b/spine/nodemanagement.go index 85a2c869..72020995 100644 --- a/spine/nodemanagement.go +++ b/spine/nodemanagement.go @@ -19,10 +19,10 @@ func NodeManagementAddress(deviceAdress *model.AddressDeviceType) *model.Feature type NodeManagementImpl struct { *FeatureLocalImpl - entity *EntityLocalImpl + entity EntityLocal } -func NewNodeManagementImpl(id uint, entity *EntityLocalImpl) *NodeManagementImpl { +func NewNodeManagementImpl(id uint, entity EntityLocal) *NodeManagementImpl { f := &NodeManagementImpl{ FeatureLocalImpl: NewFeatureLocalImpl( id, entity, diff --git a/spine/subscription_manager.go b/spine/subscription_manager.go index 88bbd895..3a86ced1 100644 --- a/spine/subscription_manager.go +++ b/spine/subscription_manager.go @@ -154,7 +154,7 @@ func (c *SubscriptionManagerImpl) RemoveSubscriptionsForDevice(remoteDevice *Dev } // Remove all existing subscriptions for a given remote device entity -func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity *EntityRemoteImpl) { +func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity EntityRemote) { if remoteEntity == nil { return } From e9982c6d93adb0979b7b2b0144da49bbda2d3991 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 19:20:56 +0100 Subject: [PATCH 143/240] Introduce DeviceLocal and DeviceRemote interfaces --- features/feature.go | 4 +- spine/api.go | 81 ++++++++++++++++--- spine/binding_manager.go | 18 ++--- spine/binding_manager_test.go | 4 +- spine/device_local.go | 22 ++--- spine/device_remote.go | 12 ++- spine/device_remote_test.go | 6 +- spine/entity_local.go | 10 +-- spine/entity_remote.go | 6 +- spine/events.go | 2 +- spine/feature_local.go | 18 ++--- spine/feature_remote.go | 2 +- spine/heartbeat_manager.go | 4 +- spine/heartbeat_manager_test.go | 4 +- spine/message.go | 4 +- spine/nodemanagement.go | 4 +- spine/nodemanagement_destinationlist.go | 2 +- spine/nodemanagement_detaileddiscovery.go | 12 +-- .../nodemanagement_detaileddiscovery_test.go | 2 +- spine/nodemanagement_usecase.go | 8 +- spine/subscription_manager.go | 18 ++--- spine/subscription_manager_test.go | 4 +- 22 files changed, 155 insertions(+), 92 deletions(-) diff --git a/features/feature.go b/features/feature.go index d12b2dda..02849176 100644 --- a/features/feature.go +++ b/features/feature.go @@ -18,13 +18,13 @@ type FeatureImpl struct { localRole model.RoleType remoteRole model.RoleType - spineLocalDevice *spine.DeviceLocalImpl + spineLocalDevice spine.DeviceLocal localEntity spine.EntityLocal featureLocal spine.FeatureLocal featureRemote spine.FeatureRemote - remoteDevice *spine.DeviceRemoteImpl + remoteDevice spine.DeviceRemote remoteEntity spine.EntityRemote } diff --git a/spine/api.go b/spine/api.go index 0a7216ad..5dd4692b 100644 --- a/spine/api.go +++ b/spine/api.go @@ -12,6 +12,60 @@ type EventHandler interface { /* Device */ +type Device interface { + Address() *model.AddressDeviceType + DeviceType() *model.DeviceTypeType + FeatureSet() *model.NetworkManagementFeatureSetType + DestinationData() model.NodeManagementDestinationDataType +} + +type DeviceLocal interface { + Device + RemoveRemoteDeviceConnection(ski string) + AddRemoteDeviceForSki(ski string, rDevice DeviceRemote) + AddRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing + RemoveRemoteDevice(ski string) + RemoteDevices() []DeviceRemote + RemoteDeviceForAddress(address model.AddressDeviceType) DeviceRemote + RemoteDeviceForSki(ski string) DeviceRemote + ProcessCmd(datagram model.DatagramType, remoteDevice DeviceRemote) error + NodeManagement() *NodeManagementImpl + SubscriptionManager() SubscriptionManager + BindingManager() BindingManager + HeartbeatManager() HeartbeatManager + AddEntity(entity EntityLocal) + RemoveEntity(entity EntityLocal) + Entities() []EntityLocal + Entity(id []model.AddressEntityType) EntityLocal + EntityForType(entityType model.EntityTypeType) EntityLocal + FeatureByAddress(address *model.FeatureAddressType) FeatureLocal + NotifySubscribers(featureAddress *model.FeatureAddressType, cmd model.CmdType) + Information() *model.NodeManagementDetailedDiscoveryDeviceInformationType +} + +type DeviceRemote interface { + Device + Ski() string + SetAddress(address *model.AddressDeviceType) + HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) + Sender() Sender + Entity(id []model.AddressEntityType) EntityRemote + Entities() []EntityRemote + FeatureByAddress(address *model.FeatureAddressType) FeatureRemote + RemoveByAddress(addr []model.AddressEntityType) EntityRemote + FeatureByEntityTypeAndRole(entity EntityRemote, featureType model.FeatureTypeType, role model.RoleType) FeatureRemote + UpdateDevice(description *model.NetworkManagementDeviceDescriptionDataType) + AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]EntityRemote, error) + AddEntity(entity EntityRemote) EntityRemote + VerifyUseCaseScenariosAndFeaturesSupport( + usecaseActor model.UseCaseActorType, + usecaseName model.UseCaseNameType, + scenarios []model.UseCaseScenarioSupportType, + serverFeatures []model.FeatureTypeType, + ) bool + CheckEntityInformation(initialData bool, entity model.NodeManagementDetailedDiscoveryEntityInformationType) error +} + // implemented by spine.DeviceLocalImpl and used by shipConnection type DeviceLocalConnection interface { RemoveRemoteDeviceConnection(ski string) @@ -30,7 +84,7 @@ type Entity interface { type EntityLocal interface { Entity - Device() *DeviceLocalImpl + Device() DeviceLocal AddFeature(f FeatureLocal) GetOrAddFeature(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal FeatureOfTypeAndRole(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal @@ -56,7 +110,7 @@ type EntityLocal interface { type EntityRemote interface { Entity - Device() *DeviceRemoteImpl + Device() DeviceRemote AddFeature(f FeatureRemote) Features() []FeatureRemote Feature(addressFeature *model.AddressFeatureType) FeatureRemote @@ -77,8 +131,9 @@ type FeatureRemote interface { DataCopy(function model.FunctionType) any SetData(function model.FunctionType, data any) UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType) + SetDescription(desc *model.DescriptionType) Sender() Sender - Device() *DeviceRemoteImpl + Device() DeviceRemote Entity() EntityRemote SetOperations(functions []model.FunctionPropertyType) SetMaxResponseDelay(delay *model.MaxResponseDelayType) @@ -113,11 +168,11 @@ type FeatureLocal interface { elements any, destination FeatureRemote) (any, *model.ErrorType) Subscribe(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) - // SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed + // SubscribeAndWait(remoteDevice DeviceRemote, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed RemoveSubscription(remoteAddress *model.FeatureAddressType) RemoveAllSubscriptions() Bind(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) - // BindAndWait(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) *ErrorType + // BindAndWait(remoteDevice DeviceRemote, remoteAddress *model.FeatureAddressType) *ErrorType RemoveBinding(remoteAddress *model.FeatureAddressType) RemoveAllBindings() NotifyData( @@ -202,22 +257,22 @@ type PendingRequests interface { // implemented by spine.BindingManagerImpl type BindingManager interface { - AddBinding(remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error - RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error - RemoveBindingsForDevice(remoteDevice *DeviceRemoteImpl) + AddBinding(remoteDevice DeviceRemote, data model.BindingManagementRequestCallType) error + RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice DeviceRemote) error + RemoveBindingsForDevice(remoteDevice DeviceRemote) RemoveBindingsForEntity(remoteEntity EntityRemote) - Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry + Bindings(remoteDevice DeviceRemote) []*BindingEntry BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry } /* Subscription Manager */ type SubscriptionManager interface { - AddSubscription(remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error - RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error - RemoveSubscriptionsForDevice(remoteDevice *DeviceRemoteImpl) + AddSubscription(remoteDevice DeviceRemote, data model.SubscriptionManagementRequestCallType) error + RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice DeviceRemote) error + RemoveSubscriptionsForDevice(remoteDevice DeviceRemote) RemoveSubscriptionsForEntity(remoteEntity EntityRemote) - Subscriptions(remoteDevice *DeviceRemoteImpl) []*SubscriptionEntry + Subscriptions(remoteDevice DeviceRemote) []*SubscriptionEntry SubscriptionsOnFeature(featureAddress model.FeatureAddressType) []*SubscriptionEntry } diff --git a/spine/binding_manager.go b/spine/binding_manager.go index b46cad76..0b1cac45 100644 --- a/spine/binding_manager.go +++ b/spine/binding_manager.go @@ -19,7 +19,7 @@ type BindingEntry struct { } type BindingManagerImpl struct { - localDevice *DeviceLocalImpl + localDevice DeviceLocal bindingNum uint64 bindingEntries []*BindingEntry @@ -28,7 +28,7 @@ type BindingManagerImpl struct { // TODO: add persistence } -func NewBindingManager(localDevice *DeviceLocalImpl) BindingManager { +func NewBindingManager(localDevice DeviceLocal) *BindingManagerImpl { c := &BindingManagerImpl{ bindingNum: 0, localDevice: localDevice, @@ -38,7 +38,7 @@ func NewBindingManager(localDevice *DeviceLocalImpl) BindingManager { } // is sent from the client (remote device) to the server (local device) -func (c *BindingManagerImpl) AddBinding(remoteDevice *DeviceRemoteImpl, data model.BindingManagementRequestCallType) error { +func (c *BindingManagerImpl) AddBinding(remoteDevice DeviceRemote, data model.BindingManagementRequestCallType) error { serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) if serverFeature == nil { @@ -74,7 +74,7 @@ func (c *BindingManagerImpl) AddBinding(remoteDevice *DeviceRemoteImpl, data mod c.bindingEntries = append(c.bindingEntries, bindingEntry) payload := EventPayload{ - Ski: remoteDevice.ski, + Ski: remoteDevice.Ski(), EventType: EventTypeBindingChange, ChangeType: ElementChangeAdd, Data: data, @@ -85,7 +85,7 @@ func (c *BindingManagerImpl) AddBinding(remoteDevice *DeviceRemoteImpl, data mod return nil } -func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error { +func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice DeviceRemote) error { var newBindingEntries []*BindingEntry // according to the spec 7.4.4 @@ -129,7 +129,7 @@ func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCal c.bindingEntries = newBindingEntries payload := EventPayload{ - Ski: remoteDevice.ski, + Ski: remoteDevice.Ski(), EventType: EventTypeBindingChange, ChangeType: ElementChangeRemove, Data: data, @@ -142,7 +142,7 @@ func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCal } // Remove all existing bindings for a given remote device -func (c *BindingManagerImpl) RemoveBindingsForDevice(remoteDevice *DeviceRemoteImpl) { +func (c *BindingManagerImpl) RemoveBindingsForDevice(remoteDevice DeviceRemote) { if remoteDevice == nil { return } @@ -170,7 +170,7 @@ func (c *BindingManagerImpl) RemoveBindingsForEntity(remoteEntity EntityRemote) clientFeature := remoteEntity.Feature(item.clientFeature.Address().Feature) payload := EventPayload{ - Ski: remoteEntity.Device().ski, + Ski: remoteEntity.Device().Ski(), EventType: EventTypeBindingChange, ChangeType: ElementChangeRemove, Entity: remoteEntity, @@ -182,7 +182,7 @@ func (c *BindingManagerImpl) RemoveBindingsForEntity(remoteEntity EntityRemote) c.bindingEntries = newBindingEntries } -func (c *BindingManagerImpl) Bindings(remoteDevice *DeviceRemoteImpl) []*BindingEntry { +func (c *BindingManagerImpl) Bindings(remoteDevice DeviceRemote) []*BindingEntry { var result []*BindingEntry c.mux.Lock() diff --git a/spine/binding_manager_test.go b/spine/binding_manager_test.go index b741bf48..f9b9fbbe 100644 --- a/spine/binding_manager_test.go +++ b/spine/binding_manager_test.go @@ -17,8 +17,8 @@ func TestBindingManagerSuite(t *testing.T) { type BindingManagerSuite struct { suite.Suite - localDevice *DeviceLocalImpl - remoteDevice *DeviceRemoteImpl + localDevice DeviceLocal + remoteDevice DeviceRemote sut BindingManager } diff --git a/spine/device_local.go b/spine/device_local.go index 1900c595..832d5048 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -12,6 +12,8 @@ import ( "github.com/enbility/eebus-go/util" ) +var _ DeviceLocal = (*DeviceLocalImpl)(nil) + type DeviceLocalImpl struct { *DeviceImpl entities []EntityLocal @@ -20,7 +22,7 @@ type DeviceLocalImpl struct { heartbeatManager HeartbeatManager nodeManagement *NodeManagementImpl - remoteDevices map[string]*DeviceRemoteImpl + remoteDevices map[string]DeviceRemote brandName string deviceModel string @@ -45,7 +47,7 @@ func NewDeviceLocalImpl(brandName, deviceModel, serialNumber, deviceCode, device res := &DeviceLocalImpl{ DeviceImpl: NewDeviceImpl(&address, &deviceType, fSet), - remoteDevices: make(map[string]*DeviceRemoteImpl), + remoteDevices: make(map[string]DeviceRemote), brandName: brandName, deviceModel: deviceModel, serialNumber: serialNumber, @@ -78,7 +80,7 @@ func (r *DeviceLocalImpl) RemoveRemoteDeviceConnection(ski string) { } // Helper method used by tests and AddRemoteDevice -func (r *DeviceLocalImpl) AddRemoteDeviceForSki(ski string, rDevice *DeviceRemoteImpl) { +func (r *DeviceLocalImpl) AddRemoteDeviceForSki(ski string, rDevice DeviceRemote) { r.mux.Lock() r.remoteDevices[ski] = rDevice r.mux.Unlock() @@ -127,7 +129,7 @@ func (r *DeviceLocalImpl) HandleEvent(payload EventPayload) { _, _ = r.nodeManagement.Subscribe(payload.Feature.Address()) // Request Use Case Data - _, _ = r.nodeManagement.RequestUseCaseData(payload.Device.ski, payload.Device.Address(), payload.Device.Sender()) + _, _ = r.nodeManagement.RequestUseCaseData(payload.Device.Ski(), payload.Device.Address(), payload.Device.Sender()) } } @@ -158,11 +160,11 @@ func (r *DeviceLocalImpl) RemoveRemoteDevice(ski string) { } } -func (r *DeviceLocalImpl) RemoteDevices() []*DeviceRemoteImpl { +func (r *DeviceLocalImpl) RemoteDevices() []DeviceRemote { r.mux.Lock() defer r.mux.Unlock() - res := make([]*DeviceRemoteImpl, 0) + res := make([]DeviceRemote, 0) for _, rDevice := range r.remoteDevices { res = append(res, rDevice) } @@ -170,12 +172,12 @@ func (r *DeviceLocalImpl) RemoteDevices() []*DeviceRemoteImpl { return res } -func (r *DeviceLocalImpl) RemoteDeviceForAddress(address model.AddressDeviceType) *DeviceRemoteImpl { +func (r *DeviceLocalImpl) RemoteDeviceForAddress(address model.AddressDeviceType) DeviceRemote { r.mux.Lock() defer r.mux.Unlock() for _, item := range r.remoteDevices { - if *item.address == address { + if *item.Address() == address { return item } } @@ -183,14 +185,14 @@ func (r *DeviceLocalImpl) RemoteDeviceForAddress(address model.AddressDeviceType return nil } -func (r *DeviceLocalImpl) RemoteDeviceForSki(ski string) *DeviceRemoteImpl { +func (r *DeviceLocalImpl) RemoteDeviceForSki(ski string) DeviceRemote { r.mux.Lock() defer r.mux.Unlock() return r.remoteDevices[ski] } -func (r *DeviceLocalImpl) ProcessCmd(datagram model.DatagramType, remoteDevice *DeviceRemoteImpl) error { +func (r *DeviceLocalImpl) ProcessCmd(datagram model.DatagramType, remoteDevice DeviceRemote) error { destAddr := datagram.Header.AddressDestination localFeature := r.FeatureByAddress(destAddr) diff --git a/spine/device_remote.go b/spine/device_remote.go index dde7a529..794bbb88 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -11,6 +11,8 @@ import ( "github.com/enbility/eebus-go/spine/model" ) +var _ DeviceRemote = (*DeviceRemoteImpl)(nil) + type DeviceRemoteImpl struct { *DeviceImpl @@ -21,12 +23,12 @@ type DeviceRemoteImpl struct { sender Sender - localDevice *DeviceLocalImpl + localDevice DeviceLocal } var _ SpineDataProcessing = (*DeviceRemoteImpl)(nil) -func NewDeviceRemoteImpl(localDevice *DeviceLocalImpl, ski string, sender Sender) *DeviceRemoteImpl { +func NewDeviceRemoteImpl(localDevice DeviceLocal, ski string, sender Sender) *DeviceRemoteImpl { res := DeviceRemoteImpl{ DeviceImpl: NewDeviceImpl(nil, nil, nil), ski: ski, @@ -43,6 +45,10 @@ func (d *DeviceRemoteImpl) Ski() string { return d.ski } +func (d *DeviceRemoteImpl) SetAddress(address *model.AddressDeviceType) { + d.address = address +} + // // this connection is closed // func (d *DeviceRemoteImpl) CloseConnection() { // } @@ -322,7 +328,7 @@ func (d *DeviceRemoteImpl) VerifyUseCaseScenariosAndFeaturesSupport( func unmarshalFeature(entity EntityRemote, featureData model.NodeManagementDetailedDiscoveryFeatureInformationType, ) (FeatureRemote, bool) { - var result *FeatureRemoteImpl + var result FeatureRemote fid := featureData.Description diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index 637113bb..2dfc9c2f 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -21,8 +21,8 @@ func TestDeviceRemoteSuite(t *testing.T) { type DeviceRemoteSuite struct { suite.Suite - localDevice *DeviceLocalImpl - remoteDevice *DeviceRemoteImpl + localDevice DeviceLocal + remoteDevice DeviceRemote remoteEntity EntityRemote } @@ -36,7 +36,7 @@ func (s *DeviceRemoteSuite) BeforeTest(suiteName, testName string) { ski := "test" sender := NewSender(s) s.remoteDevice = NewDeviceRemoteImpl(s.localDevice, ski, sender) - s.remoteDevice.address = util.Ptr(model.AddressDeviceType("test")) + s.remoteDevice.SetAddress(util.Ptr(model.AddressDeviceType("test"))) s.localDevice.AddRemoteDevice(ski, s) s.remoteEntity = NewEntityRemoteImpl(s.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) diff --git a/spine/entity_local.go b/spine/entity_local.go index c5a4dfa0..68143e0a 100644 --- a/spine/entity_local.go +++ b/spine/entity_local.go @@ -8,18 +8,18 @@ var _ EntityLocal = (*EntityLocalImpl)(nil) type EntityLocalImpl struct { *EntityImpl - device *DeviceLocalImpl + device DeviceLocal features []FeatureLocal } -func NewEntityLocalImpl(device *DeviceLocalImpl, eType model.EntityTypeType, entityAddress []model.AddressEntityType) *EntityLocalImpl { +func NewEntityLocalImpl(device DeviceLocal, eType model.EntityTypeType, entityAddress []model.AddressEntityType) *EntityLocalImpl { return &EntityLocalImpl{ EntityImpl: NewEntity(eType, device.Address(), entityAddress), device: device, } } -func (r *EntityLocalImpl) Device() *DeviceLocalImpl { +func (r *EntityLocalImpl) Device() DeviceLocal { return r.device } @@ -105,7 +105,7 @@ func (r *EntityLocalImpl) AddUseCaseSupport( useCaseAvailable bool, scenarios []model.UseCaseScenarioSupportType, ) { - nodeMgmt := r.device.nodeManagement + nodeMgmt := r.device.NodeManagement() data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) if data == nil { @@ -127,7 +127,7 @@ func (r *EntityLocalImpl) RemoveUseCaseSupport( actor model.UseCaseActorType, useCaseName model.UseCaseNameType, ) { - nodeMgmt := r.device.nodeManagement + nodeMgmt := r.device.NodeManagement() data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) if data == nil { diff --git a/spine/entity_remote.go b/spine/entity_remote.go index c0dbea83..9b9bb9e5 100644 --- a/spine/entity_remote.go +++ b/spine/entity_remote.go @@ -8,18 +8,18 @@ var _ EntityRemote = (*EntityRemoteImpl)(nil) type EntityRemoteImpl struct { *EntityImpl - device *DeviceRemoteImpl + device DeviceRemote features []FeatureRemote } -func NewEntityRemoteImpl(device *DeviceRemoteImpl, eType model.EntityTypeType, entityAddress []model.AddressEntityType) *EntityRemoteImpl { +func NewEntityRemoteImpl(device DeviceRemote, eType model.EntityTypeType, entityAddress []model.AddressEntityType) *EntityRemoteImpl { return &EntityRemoteImpl{ EntityImpl: NewEntity(eType, device.Address(), entityAddress), device: device, } } -func (r *EntityRemoteImpl) Device() *DeviceRemoteImpl { +func (r *EntityRemoteImpl) Device() DeviceRemote { return r.device } diff --git a/spine/events.go b/spine/events.go index 581125a4..53ac0f4c 100644 --- a/spine/events.go +++ b/spine/events.go @@ -37,7 +37,7 @@ type EventPayload struct { Ski string // required EventType EventType // required ChangeType ElementChangeType // required - Device *DeviceRemoteImpl // required for DetailedDiscovery Call + Device DeviceRemote // required for DetailedDiscovery Call Entity EntityRemote // required for DetailedDiscovery Call and Notify Feature FeatureRemote CmdClassifier *model.CmdClassifierType // optional, used together with EventType EventTypeDataChange diff --git a/spine/feature_local.go b/spine/feature_local.go index 9633da53..780b3cb5 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -49,7 +49,7 @@ func NewFeatureLocalImpl(id uint, entity EntityLocal, ftype model.FeatureTypeTyp return res } -func (r *FeatureLocalImpl) Device() *DeviceLocalImpl { +func (r *FeatureLocalImpl) Device() DeviceLocal { return r.entity.Device() } @@ -144,7 +144,7 @@ func (r *FeatureLocalImpl) RequestData( fd := r.functionData(function) cmd := fd.ReadCmdType(selector, elements) - return r.RequestDataBySenderAddress(cmd, destination.Sender(), destination.Device().ski, destination.Address(), destination.MaxResponseDelayDuration()) + return r.RequestDataBySenderAddress(cmd, destination.Sender(), destination.Device().Ski(), destination.Address(), destination.MaxResponseDelayDuration()) } func (r *FeatureLocalImpl) RequestDataBySenderAddress( @@ -169,7 +169,7 @@ func (r *FeatureLocalImpl) FetchRequestData( msgCounter model.MsgCounterType, destination FeatureRemote) (any, *model.ErrorType) { - return r.pendingRequests.GetData(destination.Device().ski, msgCounter) + return r.pendingRequests.GetData(destination.Device().Ski(), msgCounter) } // Send a data request for function to destination and return the response @@ -251,7 +251,7 @@ func (r *FeatureLocalImpl) RemoveAllSubscriptions() { /* TODO: check if this function is needed and can be fixed, see https://github.com/enbility/eebus-go/issues/31 // Subscribe to a remote feature and wait for the result -func (r *FeatureLocalImpl) SubscribeAndWait(remoteDevice *DeviceRemoteImpl, remoteAdress *model.FeatureAddressType) *ErrorType { +func (r *FeatureLocalImpl) SubscribeAndWait(remoteDevice DeviceRemote, remoteAdress *model.FeatureAddressType) *ErrorType { if r.Role() == model.RoleTypeServer { return NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) } @@ -336,7 +336,7 @@ func (r *FeatureLocalImpl) RemoveAllBindings() { /* TODO: check if this function is needed and can be fixed, see https://github.com/enbility/eebus-go/issues/31 // Bind to a remote feature and wait for the result -func (r *FeatureLocalImpl) BindAndWait(remoteDevice *DeviceRemoteImpl, remoteAddress *model.FeatureAddressType) *ErrorType { +func (r *FeatureLocalImpl) BindAndWait(remoteDevice DeviceRemote, remoteAddress *model.FeatureAddressType) *ErrorType { if r.Role() == model.RoleTypeServer { return NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) } @@ -441,7 +441,7 @@ func (r *FeatureLocalImpl) processResult(message *Message) *model.ErrorType { } // we don't need to populate this error as requests don't require a pendingRequest entry - _ = r.pendingRequests.SetResult(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference, model.NewErrorTypeFromResult(message.Cmd.ResultData)) + _ = r.pendingRequests.SetResult(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference, model.NewErrorTypeFromResult(message.Cmd.ResultData)) if message.RequestHeader.MsgCounterReference == nil { return nil @@ -490,13 +490,13 @@ func (r *FeatureLocalImpl) processRead(function model.FunctionType, requestHeade func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, requestHeader *model.HeaderType, featureRemote FeatureRemote) *model.ErrorType { featureRemote.UpdateData(function, data, filterPartial, filterDelete) - _ = r.pendingRequests.SetData(featureRemote.Device().ski, *requestHeader.MsgCounterReference, data) + _ = r.pendingRequests.SetData(featureRemote.Device().Ski(), *requestHeader.MsgCounterReference, data) // an error in SetData only means that there is no pendingRequest waiting for this dataset // so this is nothing to consider as an error to return // the data was updated, so send an event, other event handlers may watch out for this as well payload := EventPayload{ - Ski: featureRemote.Device().ski, + Ski: featureRemote.Device().Ski(), EventType: EventTypeDataChange, ChangeType: ElementChangeUpdate, Feature: featureRemote, @@ -514,7 +514,7 @@ func (r *FeatureLocalImpl) processNotify(function model.FunctionType, data any, featureRemote.UpdateData(function, data, filterPartial, filterDelete) payload := EventPayload{ - Ski: featureRemote.Device().ski, + Ski: featureRemote.Device().Ski(), EventType: EventTypeDataChange, ChangeType: ElementChangeUpdate, Feature: featureRemote, diff --git a/spine/feature_remote.go b/spine/feature_remote.go index 4fc5e983..adef9945 100644 --- a/spine/feature_remote.go +++ b/spine/feature_remote.go @@ -71,7 +71,7 @@ func (r *FeatureRemoteImpl) Sender() Sender { return r.Device().Sender() } -func (r *FeatureRemoteImpl) Device() *DeviceRemoteImpl { +func (r *FeatureRemoteImpl) Device() DeviceRemote { return r.entity.Device() } diff --git a/spine/heartbeat_manager.go b/spine/heartbeat_manager.go index 3c6212cc..c5926e4f 100644 --- a/spine/heartbeat_manager.go +++ b/spine/heartbeat_manager.go @@ -10,7 +10,7 @@ import ( ) type HeartbeatManagerImpl struct { - localDevice *DeviceLocalImpl + localDevice DeviceLocal localEntity *EntityLocalImpl localFeature FeatureLocal @@ -23,7 +23,7 @@ type HeartbeatManagerImpl struct { } // Create a new Heartbeat Manager which handles sending of heartbeats -func NewHeartbeatManager(localDevice *DeviceLocalImpl, subscriptionManager SubscriptionManager, timeout time.Duration) HeartbeatManager { +func NewHeartbeatManager(localDevice DeviceLocal, subscriptionManager SubscriptionManager, timeout time.Duration) HeartbeatManager { h := &HeartbeatManagerImpl{ localDevice: localDevice, subscriptionManager: subscriptionManager, diff --git a/spine/heartbeat_manager_test.go b/spine/heartbeat_manager_test.go index dbcdf392..4b5c2cb8 100644 --- a/spine/heartbeat_manager_test.go +++ b/spine/heartbeat_manager_test.go @@ -17,8 +17,8 @@ func TestHeartbeatManagerSuite(t *testing.T) { type HeartBeatManagerSuite struct { suite.Suite - localDevice *DeviceLocalImpl - remoteDevice *DeviceRemoteImpl + localDevice DeviceLocal + remoteDevice DeviceRemote sut HeartbeatManager } diff --git a/spine/message.go b/spine/message.go index 32977fd1..4f6b9c05 100644 --- a/spine/message.go +++ b/spine/message.go @@ -10,7 +10,7 @@ type Message struct { FilterDelete *model.FilterType FeatureRemote FeatureRemote EntityRemote EntityRemote - DeviceRemote *DeviceRemoteImpl + DeviceRemote DeviceRemote } type ResultMessage struct { @@ -19,5 +19,5 @@ type ResultMessage struct { FeatureLocal FeatureLocal // required, may not be nil FeatureRemote FeatureRemote // required, may not be nil EntityRemote EntityRemote // required, may not be nil - DeviceRemote *DeviceRemoteImpl // required, may not be nil + DeviceRemote DeviceRemote // required, may not be nil } diff --git a/spine/nodemanagement.go b/spine/nodemanagement.go index 72020995..14480925 100644 --- a/spine/nodemanagement.go +++ b/spine/nodemanagement.go @@ -46,7 +46,7 @@ func NewNodeManagementImpl(id uint, entity EntityLocal) *NodeManagementImpl { return f } -func (r *NodeManagementImpl) Device() *DeviceLocalImpl { +func (r *NodeManagementImpl) Device() DeviceLocal { return r.entity.Device() } @@ -54,7 +54,7 @@ func (r *NodeManagementImpl) HandleMessage(message *Message) *model.ErrorType { switch { case message.Cmd.ResultData != nil: if err := r.processResult(message); err != nil { - _ = r.pendingRequests.Remove(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference) + _ = r.pendingRequests.Remove(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference) return err } diff --git a/spine/nodemanagement_destinationlist.go b/spine/nodemanagement_destinationlist.go index 4cad3983..b7a89ea1 100644 --- a/spine/nodemanagement_destinationlist.go +++ b/spine/nodemanagement_destinationlist.go @@ -36,7 +36,7 @@ func (r *NodeManagementImpl) handleMsgDestinationListData(message *Message, data return r.processReadDestinationListData(message.FeatureRemote, message.RequestHeader) case model.CmdClassifierTypeReply: - if err := r.pendingRequests.Remove(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference); err != nil { + if err := r.pendingRequests.Remove(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference); err != nil { return errors.New(err.String()) } return r.processReplyDestinationListData(message, *data) diff --git a/spine/nodemanagement_detaileddiscovery.go b/spine/nodemanagement_detaileddiscovery.go index af3909d2..855d55c2 100644 --- a/spine/nodemanagement_detaileddiscovery.go +++ b/spine/nodemanagement_detaileddiscovery.go @@ -17,7 +17,7 @@ func (r *NodeManagementImpl) RequestDetailedDiscovery(remoteDeviceSki string, re } // handle incoming detailed discovery read call -func (r *NodeManagementImpl) processReadDetailedDiscoveryData(deviceRemote *DeviceRemoteImpl, requestHeader *model.HeaderType) error { +func (r *NodeManagementImpl) processReadDetailedDiscoveryData(deviceRemote DeviceRemote, requestHeader *model.HeaderType) error { if deviceRemote == nil { return errors.New("nodemanagement.readDetailedDiscoveryData: invalid deviceRemote") } @@ -64,7 +64,7 @@ func (r *NodeManagementImpl) processReplyDetailedDiscoveryData(message *Message, // publish event for remote device added payload := EventPayload{ - Ski: remoteDevice.ski, + Ski: remoteDevice.Ski(), EventType: EventTypeDeviceChange, ChangeType: ElementChangeAdd, Device: remoteDevice, @@ -76,7 +76,7 @@ func (r *NodeManagementImpl) processReplyDetailedDiscoveryData(message *Message, // publish event for each added remote entity for _, entity := range entities { payload := EventPayload{ - Ski: remoteDevice.ski, + Ski: remoteDevice.Ski(), EventType: EventTypeEntityChange, ChangeType: ElementChangeAdd, Device: remoteDevice, @@ -142,7 +142,7 @@ func (r *NodeManagementImpl) processNotifyDetailedDiscoveryData(message *Message // publish event for each added remote entity for _, entity := range entities { payload := EventPayload{ - Ski: remoteDevice.ski, + Ski: remoteDevice.Ski(), EventType: EventTypeEntityChange, ChangeType: ElementChangeAdd, Device: remoteDevice, @@ -184,7 +184,7 @@ func (r *NodeManagementImpl) processNotifyDetailedDiscoveryData(message *Message } payload := EventPayload{ - Ski: remoteDevice.ski, + Ski: remoteDevice.Ski(), EventType: EventTypeEntityChange, ChangeType: ElementChangeRemove, Device: remoteDevice, @@ -251,7 +251,7 @@ func (r *NodeManagementImpl) handleMsgDetailedDiscoveryData(message *Message, da return r.processReadDetailedDiscoveryData(message.DeviceRemote, message.RequestHeader) case model.CmdClassifierTypeReply: - if err := r.pendingRequests.Remove(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference); err != nil { + if err := r.pendingRequests.Remove(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference); err != nil { return errors.New(err.String()) } return r.processReplyDetailedDiscoveryData(message, data) diff --git a/spine/nodemanagement_detaileddiscovery_test.go b/spine/nodemanagement_detaileddiscovery_test.go index 70f670ea..938c532d 100644 --- a/spine/nodemanagement_detaileddiscovery_test.go +++ b/spine/nodemanagement_detaileddiscovery_test.go @@ -27,7 +27,7 @@ func TestNodeManagementSuite(t *testing.T) { type NodeManagementSuite struct { suite.Suite - sut *DeviceLocalImpl + sut DeviceLocal remoteSki string diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go index b89bcf06..3e1f4277 100644 --- a/spine/nodemanagement_usecase.go +++ b/spine/nodemanagement_usecase.go @@ -16,8 +16,8 @@ func (r *NodeManagementImpl) RequestUseCaseData(remoteDeviceSki string, remoteDe return r.RequestDataBySenderAddress(cmd, sender, remoteDeviceSki, rfAdress, defaultMaxResponseDelay) } -func (r *NodeManagementImpl) NotifyUseCaseData(remoteDevice *DeviceRemoteImpl) (*model.MsgCounterType, error) { - rfAdress := featureAddressType(NodeManagementFeatureId, EntityAddressType(remoteDevice.address, DeviceInformationAddressEntity)) +func (r *NodeManagementImpl) NotifyUseCaseData(remoteDevice DeviceRemote) (*model.MsgCounterType, error) { + rfAdress := featureAddressType(NodeManagementFeatureId, EntityAddressType(remoteDevice.Address(), DeviceInformationAddressEntity)) rEntity := remoteDevice.Entity([]model.AddressEntityType{model.AddressEntityType(DeviceInformationEntityId)}) featureRemote := remoteDevice.FeatureByEntityTypeAndRole(rEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) @@ -39,7 +39,7 @@ func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data *mod // the data was updated, so send an event, other event handlers may watch out for this as well payload := EventPayload{ - Ski: message.FeatureRemote.Device().ski, + Ski: message.FeatureRemote.Device().Ski(), EventType: EventTypeDataChange, ChangeType: ElementChangeUpdate, Feature: message.FeatureRemote, @@ -59,7 +59,7 @@ func (r *NodeManagementImpl) handleMsgUseCaseData(message *Message, data *model. return r.processReadUseCaseData(message.FeatureRemote, message.RequestHeader) case model.CmdClassifierTypeReply: - if err := r.pendingRequests.Remove(message.DeviceRemote.ski, *message.RequestHeader.MsgCounterReference); err != nil { + if err := r.pendingRequests.Remove(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference); err != nil { return errors.New(err.String()) } return r.processReplyUseCaseData(message, data) diff --git a/spine/subscription_manager.go b/spine/subscription_manager.go index 3a86ced1..093b4c64 100644 --- a/spine/subscription_manager.go +++ b/spine/subscription_manager.go @@ -19,7 +19,7 @@ type SubscriptionEntry struct { } type SubscriptionManagerImpl struct { - localDevice *DeviceLocalImpl + localDevice DeviceLocal subscriptionNum uint64 subscriptionEntries []*SubscriptionEntry @@ -28,7 +28,7 @@ type SubscriptionManagerImpl struct { // TODO: add persistence } -func NewSubscriptionManager(localDevice *DeviceLocalImpl) SubscriptionManager { +func NewSubscriptionManager(localDevice DeviceLocal) *SubscriptionManagerImpl { c := &SubscriptionManagerImpl{ subscriptionNum: 0, localDevice: localDevice, @@ -38,7 +38,7 @@ func NewSubscriptionManager(localDevice *DeviceLocalImpl) SubscriptionManager { } // is sent from the client (remote device) to the server (local device) -func (c *SubscriptionManagerImpl) AddSubscription(remoteDevice *DeviceRemoteImpl, data model.SubscriptionManagementRequestCallType) error { +func (c *SubscriptionManagerImpl) AddSubscription(remoteDevice DeviceRemote, data model.SubscriptionManagementRequestCallType) error { serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) if serverFeature == nil { @@ -74,7 +74,7 @@ func (c *SubscriptionManagerImpl) AddSubscription(remoteDevice *DeviceRemoteImpl c.subscriptionEntries = append(c.subscriptionEntries, subscriptionEntry) payload := EventPayload{ - Ski: remoteDevice.ski, + Ski: remoteDevice.Ski(), EventType: EventTypeSubscriptionChange, ChangeType: ElementChangeAdd, Data: data, @@ -86,7 +86,7 @@ func (c *SubscriptionManagerImpl) AddSubscription(remoteDevice *DeviceRemoteImpl } // Remove a specific subscription that is provided by a delete message from a remote device -func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice *DeviceRemoteImpl) error { +func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice DeviceRemote) error { var newSubscriptionEntries []*SubscriptionEntry // according to the spec 7.4.4 @@ -130,7 +130,7 @@ func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionMana c.subscriptionEntries = newSubscriptionEntries payload := EventPayload{ - Ski: remoteDevice.ski, + Ski: remoteDevice.Ski(), EventType: EventTypeSubscriptionChange, ChangeType: ElementChangeRemove, Data: data, @@ -143,7 +143,7 @@ func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionMana } // Remove all existing subscriptions for a given remote device -func (c *SubscriptionManagerImpl) RemoveSubscriptionsForDevice(remoteDevice *DeviceRemoteImpl) { +func (c *SubscriptionManagerImpl) RemoveSubscriptionsForDevice(remoteDevice DeviceRemote) { if remoteDevice == nil { return } @@ -171,7 +171,7 @@ func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity Enti clientFeature := remoteEntity.Feature(item.clientFeature.Address().Feature) payload := EventPayload{ - Ski: remoteEntity.Device().ski, + Ski: remoteEntity.Device().Ski(), EventType: EventTypeSubscriptionChange, ChangeType: ElementChangeRemove, Entity: remoteEntity, @@ -183,7 +183,7 @@ func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity Enti c.subscriptionEntries = newSubscriptionEntries } -func (c *SubscriptionManagerImpl) Subscriptions(remoteDevice *DeviceRemoteImpl) []*SubscriptionEntry { +func (c *SubscriptionManagerImpl) Subscriptions(remoteDevice DeviceRemote) []*SubscriptionEntry { var result []*SubscriptionEntry c.mux.Lock() diff --git a/spine/subscription_manager_test.go b/spine/subscription_manager_test.go index c3cdd9ab..c14ae58b 100644 --- a/spine/subscription_manager_test.go +++ b/spine/subscription_manager_test.go @@ -17,8 +17,8 @@ func TestSubscriptionManagerSuite(t *testing.T) { type SubscriptionManagerSuite struct { suite.Suite - localDevice *DeviceLocalImpl - remoteDevice *DeviceRemoteImpl + localDevice DeviceLocal + remoteDevice DeviceRemote sut SubscriptionManager } From dc549ca138098bf0cc949c9e7e6039420459b615 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 19:43:44 +0100 Subject: [PATCH 144/240] Add function to get remote device usecases --- spine/api.go | 1 + spine/device_remote.go | 10 ++++++++++ spine/device_remote_test.go | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/spine/api.go b/spine/api.go index 5dd4692b..e88e0f1e 100644 --- a/spine/api.go +++ b/spine/api.go @@ -57,6 +57,7 @@ type DeviceRemote interface { UpdateDevice(description *model.NetworkManagementDeviceDescriptionDataType) AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]EntityRemote, error) AddEntity(entity EntityRemote) EntityRemote + UseCases() []model.UseCaseInformationDataType VerifyUseCaseScenariosAndFeaturesSupport( usecaseActor model.UseCaseActorType, usecaseName model.UseCaseNameType, diff --git a/spine/device_remote.go b/spine/device_remote.go index 794bbb88..083a375f 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -241,6 +241,16 @@ func (d *DeviceRemoteImpl) AddEntity(entity EntityRemote) EntityRemote { return entity } +func (d *DeviceRemoteImpl) UseCases() []model.UseCaseInformationDataType { + entity := d.Entity(DeviceInformationAddressEntity) + + nodemgmt := d.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + + data := nodemgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + + return data.UseCaseInformation +} + // 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( diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index 2dfc9c2f..7f9c7bfd 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -80,6 +80,13 @@ func (s *DeviceRemoteSuite) Test_FeatureByEntityTypeAndRole() { assert.Nil(s.T(), feature) } +func (s *DeviceRemoteSuite) Test_Usecases() { + _, _ = s.remoteDevice.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) + + uc := s.remoteDevice.UseCases() + assert.NotNil(s.T(), uc) +} + func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport_ElliJSON() { _, _ = s.remoteDevice.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) From 8e0372a5821c0f43fb77daeb9368811f28d9a95d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Jan 2024 19:51:37 +0100 Subject: [PATCH 145/240] Update Feature interface --- spine/api.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spine/api.go b/spine/api.go index e88e0f1e..85a515b9 100644 --- a/spine/api.go +++ b/spine/api.go @@ -125,6 +125,10 @@ type Feature interface { Type() model.FeatureTypeType Role() model.RoleType Operations() map[model.FunctionType]*Operations + Description() *model.DescriptionType + SetDescription(desc *model.DescriptionType) + SetDescriptionString(s string) + String() string } type FeatureRemote interface { @@ -132,7 +136,6 @@ type FeatureRemote interface { DataCopy(function model.FunctionType) any SetData(function model.FunctionType, data any) UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType) - SetDescription(desc *model.DescriptionType) Sender() Sender Device() DeviceRemote Entity() EntityRemote From eb9a1f604a40363113681a9435bc722cbc7baf34 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 13:10:01 +0100 Subject: [PATCH 146/240] Fix handling usecases not being available yet --- spine/device_remote.go | 5 ++++- spine/device_remote_test.go | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/spine/device_remote.go b/spine/device_remote.go index 083a375f..29bf8cde 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -247,8 +247,11 @@ func (d *DeviceRemoteImpl) UseCases() []model.UseCaseInformationDataType { nodemgmt := d.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) data := nodemgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) + if data != nil { + return data.UseCaseInformation + } - return data.UseCaseInformation + return nil } // Checks if the given actor, usecasename and provided server features are available diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index 7f9c7bfd..4f11f1b8 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -81,9 +81,12 @@ func (s *DeviceRemoteSuite) Test_FeatureByEntityTypeAndRole() { } func (s *DeviceRemoteSuite) Test_Usecases() { + uc := s.remoteDevice.UseCases() + assert.Nil(s.T(), uc) + _, _ = s.remoteDevice.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) - uc := s.remoteDevice.UseCases() + uc = s.remoteDevice.UseCases() assert.NotNil(s.T(), uc) } From 162bcd4a9037959b7f91d2ad0f32bd8acbecaaa1 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 18:51:19 +0100 Subject: [PATCH 147/240] Remove unused and commented code --- spine/api.go | 5 --- spine/feature_local.go | 72 ------------------------------------------ 2 files changed, 77 deletions(-) diff --git a/spine/api.go b/spine/api.go index 85a515b9..36ab29ae 100644 --- a/spine/api.go +++ b/spine/api.go @@ -166,11 +166,6 @@ type FeatureLocal interface { FetchRequestData( msgCounter model.MsgCounterType, destination FeatureRemote) (any, *model.ErrorType) - RequestAndFetchData( - function model.FunctionType, - selector any, - elements any, - destination FeatureRemote) (any, *model.ErrorType) Subscribe(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) // SubscribeAndWait(remoteDevice DeviceRemote, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed RemoveSubscription(remoteAddress *model.FeatureAddressType) diff --git a/spine/feature_local.go b/spine/feature_local.go index 780b3cb5..56695312 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -172,22 +172,6 @@ func (r *FeatureLocalImpl) FetchRequestData( return r.pendingRequests.GetData(destination.Device().Ski(), msgCounter) } -// Send a data request for function to destination and return the response -// this will block until the response is received -func (r *FeatureLocalImpl) RequestAndFetchData( - function model.FunctionType, - selector any, - elements any, - destination FeatureRemote) (any, *model.ErrorType) { - - msgCounter, err := r.RequestData(function, selector, elements, destination) - if err != nil { - return nil, err - } - - return r.FetchRequestData(*msgCounter, destination) -} - // Subscribe to a remote feature func (r *FeatureLocalImpl) Subscribe(remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { if remoteAddress.Device == nil { @@ -248,34 +232,6 @@ func (r *FeatureLocalImpl) RemoveAllSubscriptions() { } } -/* -TODO: check if this function is needed and can be fixed, see https://github.com/enbility/eebus-go/issues/31 -// Subscribe to a remote feature and wait for the result -func (r *FeatureLocalImpl) SubscribeAndWait(remoteDevice DeviceRemote, remoteAdress *model.FeatureAddressType) *ErrorType { - if r.Role() == model.RoleTypeServer { - return NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) - } - - msgCounter, err := remoteDevice.Sender().Subscribe(r.Address(), remoteAdress, r.ftype) - if err != nil { - return NewErrorTypeFromString(err.Error()) - } - - maxDelay := defaultMaxResponseDelay - rf := remoteDevice.FeatureByAddress(NodeManagementAddress(remoteDevice.Address())) - if rf != nil { - maxDelay = rf.MaxResponseDelayDuration() - } - - r.pendingRequests.Add(*msgCounter, maxDelay) - // this will block the go routine until the response is procedded - _, result := r.pendingRequests.GetData(*msgCounter) - // TODO: activate polling when subscription failed - - return result -} -*/ - // Bind to a remote feature func (r *FeatureLocalImpl) Bind(remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) @@ -333,34 +289,6 @@ func (r *FeatureLocalImpl) RemoveAllBindings() { } } -/* -TODO: check if this function is needed and can be fixed, see https://github.com/enbility/eebus-go/issues/31 -// Bind to a remote feature and wait for the result -func (r *FeatureLocalImpl) BindAndWait(remoteDevice DeviceRemote, remoteAddress *model.FeatureAddressType) *ErrorType { - if r.Role() == model.RoleTypeServer { - return NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) - } - - msgCounter, err := remoteDevice.Sender().Bind(r.Address(), remoteAddress, r.ftype) - if err != nil { - return NewErrorTypeFromString(err.Error()) - } - - maxDelay := defaultMaxResponseDelay - rf := remoteDevice.FeatureByAddress(remoteAddress) - if rf != nil { - maxDelay = rf.MaxResponseDelayDuration() - } - - r.pendingRequests.Add(*msgCounter, maxDelay) - // this will block the go routine until the response is procedded - _, result := r.pendingRequests.GetData(*msgCounter) - // TODO: activate polling when binding failed - - return result -} -*/ - // Send a notification message with the current data of function to the destination func (r *FeatureLocalImpl) NotifyData( function model.FunctionType, From 573ebf9c1f8c8a83a237bbf82a17ff83b6ec77cc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 18:54:16 +0100 Subject: [PATCH 148/240] Move Feature interface into api.go --- features/api.go | 11 +++++++++++ features/feature.go | 5 ----- 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 features/api.go diff --git a/features/api.go b/features/api.go new file mode 100644 index 00000000..666937ac --- /dev/null +++ b/features/api.go @@ -0,0 +1,11 @@ +package features + +import ( + "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" +) + +type Feature interface { + SubscribeForEntity() error + AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg spine.ResultMessage)) +} diff --git a/features/feature.go b/features/feature.go index 02849176..bb04b416 100644 --- a/features/feature.go +++ b/features/feature.go @@ -7,11 +7,6 @@ import ( "github.com/enbility/eebus-go/spine/model" ) -type Feature interface { - SubscribeForEntity() error - AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg spine.ResultMessage)) -} - type FeatureImpl struct { featureType model.FeatureTypeType From 19773375354ccf9b970204c28d4b2420a45cf71e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 19:54:03 +0100 Subject: [PATCH 149/240] Add spine feature local tests --- spine/api.go | 2 + spine/feature_local_test.go | 105 ++++++++++++++++++++++++++--------- spine/feature_remote_test.go | 19 ------- spine/helper_test.go | 30 ++++++++++ spine/nodemanagement_test.go | 8 +-- 5 files changed, 116 insertions(+), 48 deletions(-) delete mode 100644 spine/feature_remote_test.go diff --git a/spine/api.go b/spine/api.go index 36ab29ae..a9a01649 100644 --- a/spine/api.go +++ b/spine/api.go @@ -146,6 +146,8 @@ type FeatureRemote interface { type FeatureLocal interface { Feature + Device() DeviceLocal + Entity() EntityLocal DataCopy(function model.FunctionType) any SetData(function model.FunctionType, data any) AddResultHandler(handler FeatureResult) diff --git a/spine/feature_local_test.go b/spine/feature_local_test.go index a2323963..bb113633 100644 --- a/spine/feature_local_test.go +++ b/spine/feature_local_test.go @@ -2,7 +2,6 @@ package spine import ( "testing" - "time" "github.com/enbility/eebus-go/spine/mocks" "github.com/enbility/eebus-go/spine/model" @@ -18,29 +17,32 @@ func TestDeviceClassificationSuite(t *testing.T) { type DeviceClassificationTestSuite struct { suite.Suite - senderMock *mocks.Sender - function model.FunctionType - featureType model.FeatureTypeType - msgCounter model.MsgCounterType - remoteFeature *FeatureRemoteImpl - sut *FeatureLocalImpl + senderMock *mocks.Sender + function model.FunctionType + featureType, subFeatureType model.FeatureTypeType + msgCounter model.MsgCounterType + remoteFeature, remoteSubFeature FeatureRemote + localFeature FeatureLocal + localServerFeature FeatureLocal } -func (suite *DeviceClassificationTestSuite) SetupSuite() { +func (suite *DeviceClassificationTestSuite) BeforeTest(suiteName, testName string) { suite.senderMock = mocks.NewSender(suite.T()) suite.function = model.FunctionTypeDeviceClassificationManufacturerData suite.featureType = model.FeatureTypeTypeDeviceClassification + suite.subFeatureType = model.FeatureTypeTypeMeasurement suite.msgCounter = model.MsgCounterType(1) - suite.remoteFeature = createRemoteDeviceAndFeature(1, suite.featureType, model.RoleTypeServer, suite.senderMock) - suite.sut = createLocalDeviceAndFeature(1, suite.featureType, model.RoleTypeClient) + _, suite.remoteFeature = createRemoteDeviceAndFeature(1, suite.featureType, suite.senderMock) + _, suite.remoteSubFeature = createRemoteDeviceAndFeature(2, suite.subFeatureType, suite.senderMock) + suite.localFeature, suite.localServerFeature = createLocalDeviceAndFeature(1, suite.featureType) } func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Reply() { - suite.senderMock.On("Request", model.CmdClassifierTypeRead, suite.sut.Address(), suite.remoteFeature.Address(), false, mock.AnythingOfType("[]model.CmdType")).Return(&suite.msgCounter, nil) + suite.senderMock.On("Request", model.CmdClassifierTypeRead, suite.localFeature.Address(), suite.remoteFeature.Address(), false, mock.AnythingOfType("[]model.CmdType")).Return(&suite.msgCounter, nil) // send data request - msgCounter, err := suite.sut.RequestData(suite.function, nil, nil, suite.remoteFeature) + msgCounter, err := suite.localFeature.RequestData(suite.function, nil, nil, suite.remoteFeature) assert.Nil(suite.T(), err) manufacturerData := &model.DeviceClassificationManufacturerDataType{ @@ -63,14 +65,14 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Rep FeatureRemote: suite.remoteFeature, } // set response - msgErr := suite.sut.HandleMessage(&replyMsg) + msgErr := suite.localFeature.HandleMessage(&replyMsg) if assert.Nil(suite.T(), msgErr) { remoteData := suite.remoteFeature.DataCopy(suite.function) assert.IsType(suite.T(), &model.DeviceClassificationManufacturerDataType{}, remoteData, "Data has wrong type") } // Act - result, err := suite.sut.FetchRequestData(*msgCounter, suite.remoteFeature) + result, err := suite.localFeature.FetchRequestData(*msgCounter, suite.remoteFeature) assert.Nil(suite.T(), err) assert.NotNil(suite.T(), result) assert.IsType(suite.T(), &model.DeviceClassificationManufacturerDataType{}, result, "Data has wrong type") @@ -84,13 +86,13 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Rep } func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Error() { - suite.senderMock.On("Request", model.CmdClassifierTypeRead, suite.sut.Address(), suite.remoteFeature.Address(), false, mock.AnythingOfType("[]model.CmdType")).Return(&suite.msgCounter, nil) + suite.senderMock.On("Request", model.CmdClassifierTypeRead, suite.localFeature.Address(), suite.remoteFeature.Address(), false, mock.AnythingOfType("[]model.CmdType")).Return(&suite.msgCounter, nil) const errorNumber = model.ErrorNumberTypeGeneralError const errorDescription = "error occurred" // send data request - msgCounter, err := suite.sut.RequestData(suite.function, nil, nil, suite.remoteFeature) + msgCounter, err := suite.localFeature.RequestData(suite.function, nil, nil, suite.remoteFeature) assert.Nil(suite.T(), err) replyMsg := Message{ @@ -111,25 +113,78 @@ func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Err } // set response - msgErr := suite.sut.HandleMessage(&replyMsg) + msgErr := suite.localFeature.HandleMessage(&replyMsg) if assert.Nil(suite.T(), msgErr) { remoteData := suite.remoteFeature.DataCopy(suite.function) assert.Nil(suite.T(), remoteData) } // Act - result, err := suite.sut.FetchRequestData(*msgCounter, suite.remoteFeature) + result, err := suite.localFeature.FetchRequestData(*msgCounter, suite.remoteFeature) assert.Nil(suite.T(), result) assert.NotNil(suite.T(), err) assert.Equal(suite.T(), errorNumber, err.ErrorNumber) assert.Equal(suite.T(), errorDescription, string(*err.Description)) } -func createLocalDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType) *FeatureLocalImpl { - localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := NewEntityLocalImpl(localDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) - localDevice.AddEntity(localEntity) - localFeature := NewFeatureLocalImpl(localEntity.NextFeatureId(), localEntity, featureType, role) - localEntity.AddFeature(localFeature) - return localFeature +func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Subscribiptions() { + suite.senderMock.On("Subscribe", mock.Anything, mock.Anything, mock.Anything).Return(&suite.msgCounter, nil) + suite.senderMock.On("Unsubscribe", mock.Anything, mock.Anything, mock.Anything).Return(&suite.msgCounter, nil) + + msgCounter, err := suite.localFeature.Subscribe(suite.remoteFeature.Address()) + assert.NotNil(suite.T(), err) + assert.Nil(suite.T(), msgCounter) + + suite.localFeature.RemoveSubscription(suite.remoteFeature.Address()) + + suite.localFeature.Device().AddRemoteDeviceForSki(suite.remoteFeature.Device().Ski(), suite.remoteFeature.Device()) + + msgCounter, err = suite.localServerFeature.Subscribe(suite.remoteFeature.Address()) + assert.NotNil(suite.T(), err) + assert.Nil(suite.T(), msgCounter) + + suite.localFeature.RemoveSubscription(suite.remoteFeature.Address()) + + msgCounter, err = suite.localFeature.Subscribe(suite.remoteFeature.Address()) + assert.Nil(suite.T(), err) + assert.NotNil(suite.T(), msgCounter) + + msgCounter, err = suite.localFeature.Subscribe(suite.remoteSubFeature.Address()) + assert.Nil(suite.T(), err) + assert.NotNil(suite.T(), msgCounter) + + suite.localFeature.RemoveSubscription(suite.remoteFeature.Address()) + + suite.localFeature.RemoveAllSubscriptions() +} + +func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Bindings() { + suite.senderMock.On("Bind", mock.Anything, mock.Anything, mock.Anything).Return(&suite.msgCounter, nil) + suite.senderMock.On("Unbind", mock.Anything, mock.Anything, mock.Anything).Return(&suite.msgCounter, nil) + + msgCounter, err := suite.localFeature.Bind(suite.remoteFeature.Address()) + assert.NotNil(suite.T(), err) + assert.Nil(suite.T(), msgCounter) + + suite.localFeature.RemoveBinding(suite.remoteFeature.Address()) + + suite.localFeature.Device().AddRemoteDeviceForSki(suite.remoteFeature.Device().Ski(), suite.remoteFeature.Device()) + + msgCounter, err = suite.localServerFeature.Bind(suite.remoteFeature.Address()) + assert.NotNil(suite.T(), err) + assert.Nil(suite.T(), msgCounter) + + suite.localFeature.RemoveBinding(suite.remoteFeature.Address()) + + msgCounter, err = suite.localFeature.Bind(suite.remoteFeature.Address()) + assert.Nil(suite.T(), err) + assert.NotNil(suite.T(), msgCounter) + + msgCounter, err = suite.localFeature.Bind(suite.remoteSubFeature.Address()) + assert.Nil(suite.T(), err) + assert.NotNil(suite.T(), msgCounter) + + suite.localFeature.RemoveBinding(suite.remoteFeature.Address()) + + suite.localFeature.RemoveAllBindings() } diff --git a/spine/feature_remote_test.go b/spine/feature_remote_test.go deleted file mode 100644 index 409cb8fc..00000000 --- a/spine/feature_remote_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package spine - -import ( - "time" - - "github.com/enbility/eebus-go/spine/model" -) - -func createRemoteDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, role model.RoleType, sender Sender) *FeatureRemoteImpl { - localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - - remoteDevice := NewDeviceRemoteImpl(localDevice, "ski", sender) - // remoteDevice.address = util.Ptr(model.AddressDeviceType("Address")) - remoteEntity := NewEntityRemoteImpl(remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) - remoteDevice.AddEntity(remoteEntity) - remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, role) - remoteEntity.AddFeature(remoteFeature) - return remoteFeature -} diff --git a/spine/helper_test.go b/spine/helper_test.go index 8fe67a16..208d676e 100644 --- a/spine/helper_test.go +++ b/spine/helper_test.go @@ -6,8 +6,10 @@ import ( "os" "sync" "testing" + "time" "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/util" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" @@ -163,3 +165,31 @@ func waitForAck(t *testing.T, msgCounterReference *model.MsgCounterType, writeHa } } } + +func createLocalDeviceAndFeature(entityId uint, featureType model.FeatureTypeType) (FeatureLocal, FeatureLocal) { + localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + localDevice.address = util.Ptr(model.AddressDeviceType("Address")) + localEntity := NewEntityLocalImpl(localDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) + localDevice.AddEntity(localEntity) + localFeature := NewFeatureLocalImpl(localEntity.NextFeatureId(), localEntity, featureType, model.RoleTypeClient) + localEntity.AddFeature(localFeature) + localServerFeature := NewFeatureLocalImpl(localEntity.NextFeatureId(), localEntity, featureType, model.RoleTypeServer) + localEntity.AddFeature(localServerFeature) + + return localFeature, localServerFeature +} + +func createRemoteDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, sender Sender) (FeatureRemote, FeatureRemote) { + localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + + remoteDevice := NewDeviceRemoteImpl(localDevice, "ski", sender) + remoteDevice.address = util.Ptr(model.AddressDeviceType("Address")) + remoteEntity := NewEntityRemoteImpl(remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) + remoteDevice.AddEntity(remoteEntity) + remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, model.RoleTypeClient) + remoteEntity.AddFeature(remoteFeature) + remoteServerFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, model.RoleTypeServer) + remoteEntity.AddFeature(remoteServerFeature) + + return remoteFeature, remoteServerFeature +} diff --git a/spine/nodemanagement_test.go b/spine/nodemanagement_test.go index dce5fd71..d0000eb8 100644 --- a/spine/nodemanagement_test.go +++ b/spine/nodemanagement_test.go @@ -16,8 +16,8 @@ func TestNodemanagement_BindingCalls(t *testing.T) { senderMock := mocks.NewSender(t) - serverFeature := createLocalDeviceAndFeature(bindingEntityId, featureType, model.RoleTypeServer) - clientFeature := createRemoteDeviceAndFeature(bindingEntityId, featureType, model.RoleTypeClient, senderMock) + _, serverFeature := createLocalDeviceAndFeature(bindingEntityId, featureType) + clientFeature, _ := createRemoteDeviceAndFeature(bindingEntityId, featureType, senderMock) senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { cmd := args.Get(2).(model.CmdType) @@ -88,8 +88,8 @@ func TestNodemanagement_SubscriptionCalls(t *testing.T) { senderMock := mocks.NewSender(t) - serverFeature := createLocalDeviceAndFeature(subscriptionEntityId, featureType, model.RoleTypeServer) - clientFeature := createRemoteDeviceAndFeature(subscriptionEntityId, featureType, model.RoleTypeClient, senderMock) + _, serverFeature := createLocalDeviceAndFeature(subscriptionEntityId, featureType) + clientFeature, _ := createRemoteDeviceAndFeature(subscriptionEntityId, featureType, senderMock) senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { cmd := args.Get(2).(model.CmdType) From a14e50bad5c55b41d282f23f82d8a159563d13e1 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 20:09:57 +0100 Subject: [PATCH 150/240] More test for spine send --- spine/model/datagram_additions.go | 2 +- spine/send_test.go | 143 ++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/spine/model/datagram_additions.go b/spine/model/datagram_additions.go index f0d1080b..9898cbe9 100644 --- a/spine/model/datagram_additions.go +++ b/spine/model/datagram_additions.go @@ -9,7 +9,7 @@ func (d *DatagramType) PrintMessageOverview(send bool, localFeature, remoteFeatu transmission := "Send" device := "" - if d.Header.AddressDestination.Device != nil { + if d.Header.AddressDestination != nil && d.Header.AddressDestination.Device != nil { device = string(*d.Header.AddressDestination.Device) } if !send { diff --git a/spine/send_test.go b/spine/send_test.go index 04989353..0219c774 100644 --- a/spine/send_test.go +++ b/spine/send_test.go @@ -9,6 +9,35 @@ import ( "github.com/stretchr/testify/assert" ) +func TestSender_Reply_MsgCounter(t *testing.T) { + temp := &WriteMessageHandler{} + sut := NewSender(temp) + + senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) + destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) + requestHeader := &model.HeaderType{ + AddressSource: senderAddress, + AddressDestination: destinationAddress, + MsgCounter: util.Ptr(model.MsgCounterType(10)), + } + cmd := model.CmdType{ + ResultData: &model.ResultDataType{ErrorNumber: util.Ptr(model.ErrorNumberType(model.ErrorNumberTypeNoError))}, + } + + err := sut.Reply(requestHeader, senderAddress, cmd) + assert.NoError(t, err) + + // Act + err = sut.Reply(requestHeader, senderAddress, cmd) + assert.NoError(t, err) + expectedMsgCounter := 2 //because Notify was called twice + + sentBytes := temp.LastMessage() + var sentDatagram model.Datagram + assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) + assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) +} + func TestSender_Notify_MsgCounter(t *testing.T) { temp := &WriteMessageHandler{} sut := NewSender(temp) @@ -31,4 +60,118 @@ func TestSender_Notify_MsgCounter(t *testing.T) { var sentDatagram model.Datagram assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) + + _, err = sut.DatagramForMsgCounter(model.MsgCounterType(2)) + assert.NoError(t, err) + + _, err = sut.DatagramForMsgCounter(model.MsgCounterType(3)) + assert.Error(t, err) +} + +func TestSender_Write_MsgCounter(t *testing.T) { + temp := &WriteMessageHandler{} + sut := NewSender(temp) + + senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) + destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) + cmd := model.CmdType{ + ResultData: &model.ResultDataType{ErrorNumber: util.Ptr(model.ErrorNumberType(model.ErrorNumberTypeNoError))}, + } + + _, err := sut.Write(senderAddress, destinationAddress, cmd) + assert.NoError(t, err) + + // Act + _, err = sut.Write(senderAddress, destinationAddress, cmd) + assert.NoError(t, err) + expectedMsgCounter := 2 //because Write was called twice + + sentBytes := temp.LastMessage() + var sentDatagram model.Datagram + assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) + assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) +} + +func TestSender_Subscribe_MsgCounter(t *testing.T) { + temp := &WriteMessageHandler{} + sut := NewSender(temp) + + senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) + destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) + + _, err := sut.Subscribe(senderAddress, destinationAddress, model.FeatureTypeTypeLoadControl) + assert.NoError(t, err) + + // Act + _, err = sut.Subscribe(senderAddress, destinationAddress, model.FeatureTypeTypeLoadControl) + assert.NoError(t, err) + expectedMsgCounter := 2 //because Write was called twice + + sentBytes := temp.LastMessage() + var sentDatagram model.Datagram + assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) + assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) +} + +func TestSender_Unsubscribe_MsgCounter(t *testing.T) { + temp := &WriteMessageHandler{} + sut := NewSender(temp) + + senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) + destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) + + _, err := sut.Unsubscribe(senderAddress, destinationAddress) + assert.NoError(t, err) + + // Act + _, err = sut.Unsubscribe(senderAddress, destinationAddress) + assert.NoError(t, err) + expectedMsgCounter := 2 //because Write was called twice + + sentBytes := temp.LastMessage() + var sentDatagram model.Datagram + assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) + assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) +} + +func TestSender_Bind_MsgCounter(t *testing.T) { + temp := &WriteMessageHandler{} + sut := NewSender(temp) + + senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) + destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) + + _, err := sut.Bind(senderAddress, destinationAddress, model.FeatureTypeTypeLoadControl) + assert.NoError(t, err) + + // Act + _, err = sut.Bind(senderAddress, destinationAddress, model.FeatureTypeTypeLoadControl) + assert.NoError(t, err) + expectedMsgCounter := 2 //because Write was called twice + + sentBytes := temp.LastMessage() + var sentDatagram model.Datagram + assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) + assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) +} + +func TestSender_Unbind_MsgCounter(t *testing.T) { + temp := &WriteMessageHandler{} + sut := NewSender(temp) + + senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) + destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) + + _, err := sut.Unbind(senderAddress, destinationAddress) + assert.NoError(t, err) + + // Act + _, err = sut.Unbind(senderAddress, destinationAddress) + assert.NoError(t, err) + expectedMsgCounter := 2 //because Write was called twice + + sentBytes := temp.LastMessage() + var sentDatagram model.Datagram + assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) + assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) } From 52f14d2033f6e5fd2fe576ec428fc30244c72d8f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 20:14:14 +0100 Subject: [PATCH 151/240] Add spine operations tests --- spine/operations_test.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 spine/operations_test.go diff --git a/spine/operations_test.go b/spine/operations_test.go new file mode 100644 index 00000000..77c4e844 --- /dev/null +++ b/spine/operations_test.go @@ -0,0 +1,37 @@ +package spine_test + +import ( + "testing" + + "github.com/enbility/eebus-go/spine" + "github.com/stretchr/testify/assert" +) + +func TestOperations(t *testing.T) { + operations := spine.NewOperations(true, false) + assert.NotNil(t, operations) + + text := operations.String() + assert.NotEqual(t, 0, len(text)) + + data := operations.Information() + assert.NotNil(t, data) + + operations2 := spine.NewOperations(true, true) + assert.NotNil(t, operations2) + + text = operations2.String() + assert.NotEqual(t, 0, len(text)) + + data = operations2.Information() + assert.NotNil(t, data) + + operations3 := spine.NewOperations(false, false) + assert.NotNil(t, operations3) + + text = operations3.String() + assert.NotEqual(t, 0, len(text)) + + data = operations3.Information() + assert.NotNil(t, data) +} From 73f0368e9d01a261e34b33f66dbc519129b4a436 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 13 Jan 2024 21:15:07 +0100 Subject: [PATCH 152/240] Refactor interfaces - SHIP package should not depend on SPINE interfaces - Adjusted a few interface names to be more clear --- features/deviceclassification_test.go | 3 +- features/deviceconfiguration_test.go | 3 +- features/devicediagnosis_test.go | 3 +- features/electricalconnection_test.go | 3 +- features/helper_test.go | 125 +++++++++++- features/identification_test.go | 3 +- features/incentivetable_test.go | 3 +- features/loadcontrol_test.go | 3 +- features/measurement_test.go | 3 +- features/timeseries_test.go | 3 +- integration_tests/devicediagnosis_test.go | 16 +- .../electricalconnection_test.go | 14 +- .../emobility_measurement_test.go | 17 +- integration_tests/helper_test.go | 16 +- integration_tests/measurement_test.go | 16 +- service/hub.go | 20 +- service/hub_test.go | 4 +- ship/api.go | 69 ++++--- ship/connection.go | 39 ++-- ship/connection_test.go | 61 ++---- ship/handshake.go | 2 +- ship/hs_access_test.go | 3 +- ship/hs_helper_test.go | 16 +- ship/mock_types_test.go | 181 +++++++++++++----- ship/mocks/ShipConnection.go | 8 +- ...nnection.go => WebsocketDataConnection.go} | 20 +- ship/websocket.go | 6 +- ship/websocket_test.go | 4 +- spine/api.go | 33 +--- spine/binding_manager_test.go | 21 +- spine/device_local.go | 13 +- spine/device_local_test.go | 11 +- spine/device_remote.go | 15 +- spine/device_remote_test.go | 6 +- spine/heartbeat_manager_test.go | 2 +- spine/helper_test.go | 3 +- spine/mocks/SpineDataProcessing.go | 57 ------ spine/nodemanagement_detaileddiscovery.go | 4 +- .../nodemanagement_detaileddiscovery_test.go | 26 +-- spine/operations_test.go | 9 +- spine/send.go | 5 +- spine/subscription_manager_test.go | 2 +- 42 files changed, 497 insertions(+), 374 deletions(-) rename ship/mocks/{ShipDataConnection.go => WebsocketDataConnection.go} (63%) delete mode 100644 spine/mocks/SpineDataProcessing.go diff --git a/features/deviceclassification_test.go b/features/deviceclassification_test.go index 0a910b2f..7067355c 100644 --- a/features/deviceclassification_test.go +++ b/features/deviceclassification_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -25,7 +26,7 @@ type DeviceClassificationSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*DeviceClassificationSuite)(nil) +var _ ship.SpineDataConnection = (*DeviceClassificationSuite)(nil) func (s *DeviceClassificationSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index a91a3a1a..be7c459c 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -25,7 +26,7 @@ type DeviceConfigurationSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*DeviceConfigurationSuite)(nil) +var _ ship.SpineDataConnection = (*DeviceConfigurationSuite)(nil) func (s *DeviceConfigurationSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 101ac05d..c58ce044 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -25,7 +26,7 @@ type DeviceDiagnosisSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*DeviceDiagnosisSuite)(nil) +var _ ship.SpineDataConnection = (*DeviceDiagnosisSuite)(nil) func (s *DeviceDiagnosisSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index af8a6fa9..20ea5284 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -25,7 +26,7 @@ type ElectricalConnectionSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*ElectricalConnectionSuite)(nil) +var _ ship.SpineDataConnection = (*ElectricalConnectionSuite)(nil) func (s *ElectricalConnectionSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/helper_test.go b/features/helper_test.go index 8103cd93..1d9b8fab 100644 --- a/features/helper_test.go +++ b/features/helper_test.go @@ -1,20 +1,143 @@ package features_test import ( + "encoding/json" + "fmt" + "os" + "sync" + "testing" "time" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" ) +const ( + ec_permittedvaluesetlistdata_recv_notify_partial_file_path = "../spine/testdata/ec_permittedvaluesetlistdata_recv_notify_partial.json" + ec_descriptionlistdata_recv_reply_file_path = "../spine/testdata/ec_descriptionListData_recv_reply.json" + ec_parameterdescriptionlistdata_recv_reply_file_path = "../spine/testdata/ec_parameterDescriptionListData_recv_reply.json" + ec_subscriptionRequestCall_recv_result_file_path = "../spine/testdata/ec_subscriptionRequestCall_recv_result.json" + m_subscriptionRequestCall_recv_result_file_path = "../spine/testdata/m_subscriptionRequestCall_recv_result.json" + m_descriptionListData_recv_reply_file_path = "../spine/testdata/m_descriptionListData_recv_reply.json" + m_measurementListData_recv_notify_file_path = "../spine/testdata/m_measurementListData_recv_notify.json" +) + type featureFunctions struct { featureType model.FeatureTypeType functions []model.FunctionType } -func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, featureFunctions []featureFunctions) (spine.EntityLocal, spine.EntityRemote) { +type WriteMessageHandler struct { + sentMessages [][]byte + + mux sync.Mutex +} + +var _ ship.SpineDataConnection = (*WriteMessageHandler)(nil) + +func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { + t.mux.Lock() + defer t.mux.Unlock() + + t.sentMessages = append(t.sentMessages, message) +} + +func (t *WriteMessageHandler) LastMessage() []byte { + t.mux.Lock() + defer t.mux.Unlock() + + if len(t.sentMessages) == 0 { + return nil + } + + return t.sentMessages[len(t.sentMessages)-1] +} + +func (t *WriteMessageHandler) MessageWithReference(msgCounterReference *model.MsgCounterType) []byte { + t.mux.Lock() + defer t.mux.Unlock() + + var datagram model.Datagram + + for _, msg := range t.sentMessages { + if err := json.Unmarshal(msg, &datagram); err != nil { + return nil + } + if datagram.Datagram.Header.MsgCounterReference == nil { + continue + } + if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { + continue + } + if datagram.Datagram.Payload.Cmd[0].ResultData != nil { + continue + } + + return msg + } + + return nil +} + +func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.MsgCounterType) []byte { + t.mux.Lock() + defer t.mux.Unlock() + + var datagram model.Datagram + + for _, msg := range t.sentMessages { + if err := json.Unmarshal(msg, &datagram); err != nil { + return nil + } + if datagram.Datagram.Header.MsgCounterReference == nil { + continue + } + if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { + continue + } + if datagram.Datagram.Payload.Cmd[0].ResultData == nil { + continue + } + + return msg + } + + return nil +} + +func loadFileData(t *testing.T, fileName string) []byte { + fileData, err := os.ReadFile(fileName) + if err != nil { + t.Fatal(err) + } + + return fileData +} + +func waitForAck(t *testing.T, msgCounterReference *model.MsgCounterType, writeHandler *WriteMessageHandler) { + var datagram model.Datagram + + msg := writeHandler.ResultWithReference(msgCounterReference) + if msg == nil { + t.Fatal("acknowledge message was not sent!!") + } + + if err := json.Unmarshal(msg, &datagram); err != nil { + t.Fatal(err) + } + + cmd := datagram.Datagram.Payload.Cmd[0] + if cmd.ResultData != nil { + if cmd.ResultData.ErrorNumber != nil && uint(*cmd.ResultData.ErrorNumber) != uint(model.ErrorNumberTypeNoError) { + t.Fatal(fmt.Errorf("error '%d' result data received", uint(*cmd.ResultData.ErrorNumber))) + } + } +} + +func setupFeatures(t assert.TestingT, dataCon ship.SpineDataConnection, featureFunctions []featureFunctions) (spine.EntityLocal, spine.EntityRemote) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) diff --git a/features/identification_test.go b/features/identification_test.go index 85a93fd7..a784955f 100644 --- a/features/identification_test.go +++ b/features/identification_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -25,7 +26,7 @@ type IdentificationSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*IdentificationSuite)(nil) +var _ ship.SpineDataConnection = (*IdentificationSuite)(nil) func (s *IdentificationSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 26c26d2c..7e3510d6 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -25,7 +26,7 @@ type IncentiveTableSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*IncentiveTableSuite)(nil) +var _ ship.SpineDataConnection = (*IncentiveTableSuite)(nil) func (s *IncentiveTableSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index 09c7ba4e..212b9c71 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -25,7 +26,7 @@ type LoadControlSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*LoadControlSuite)(nil) +var _ ship.SpineDataConnection = (*LoadControlSuite)(nil) func (s *LoadControlSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/measurement_test.go b/features/measurement_test.go index c02a3583..3e722b55 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -26,7 +27,7 @@ type MeasurementSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*MeasurementSuite)(nil) +var _ ship.SpineDataConnection = (*MeasurementSuite)(nil) func (s *MeasurementSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/timeseries_test.go b/features/timeseries_test.go index bedf9e37..4766bb79 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" @@ -26,7 +27,7 @@ type TimeSeriesSuite struct { sentMessage []byte } -var _ spine.SpineDataConnection = (*TimeSeriesSuite)(nil) +var _ ship.SpineDataConnection = (*TimeSeriesSuite)(nil) func (s *TimeSeriesSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/integration_tests/devicediagnosis_test.go b/integration_tests/devicediagnosis_test.go index 7e8f804f..901cc73c 100644 --- a/integration_tests/devicediagnosis_test.go +++ b/integration_tests/devicediagnosis_test.go @@ -20,31 +20,25 @@ func TestDeviceDiagnosisSuite(t *testing.T) { type DeviceDiagnosisSuite struct { suite.Suite - sut *spine.DeviceLocalImpl + sut spine.DeviceLocal remoteSki string - readHandler spine.SpineDataProcessing + remoteDevice spine.DeviceRemote writeHandler *WriteMessageHandler } -func (s *DeviceDiagnosisSuite) SetupSuite() { -} - func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) { - s.sut, s.remoteSki, s.readHandler, s.writeHandler = beforeTest(suiteName, testName, 1, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + s.sut, s.remoteSki, s.remoteDevice, s.writeHandler = beforeTest(suiteName, testName, 1, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) // f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) - initialCommunication(s.T(), s.readHandler, s.writeHandler) -} - -func (s *DeviceDiagnosisSuite) AfterTest(suiteName, testName string) { + initialCommunication(s.T(), s.remoteDevice, s.writeHandler) } func (s *DeviceDiagnosisSuite) TestHeartbeatSubscription_RecvNotify() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), dd_subscriptionRequestCall_recv_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), dd_subscriptionRequestCall_recv_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Assert diff --git a/integration_tests/electricalconnection_test.go b/integration_tests/electricalconnection_test.go index 27ffb415..cd3477cb 100644 --- a/integration_tests/electricalconnection_test.go +++ b/integration_tests/electricalconnection_test.go @@ -22,11 +22,11 @@ func TestElectricalConnectionSuite(t *testing.T) { type ElectricalConnectionSuite struct { suite.Suite - sut *spine.DeviceLocalImpl + sut spine.DeviceLocal remoteSki string - readHandler spine.SpineDataProcessing + remoteDevice spine.DeviceRemote writeHandler *WriteMessageHandler } @@ -34,8 +34,8 @@ func (s *ElectricalConnectionSuite) SetupSuite() { } func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) { - s.sut, s.remoteSki, s.readHandler, s.writeHandler = beforeTest(suiteName, testName, 1, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - initialCommunication(s.T(), s.readHandler, s.writeHandler) + s.sut, s.remoteSki, s.remoteDevice, s.writeHandler = beforeTest(suiteName, testName, 1, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + initialCommunication(s.T(), s.remoteDevice, s.writeHandler) } func (s *ElectricalConnectionSuite) AfterTest(suiteName, testName string) { @@ -43,7 +43,7 @@ func (s *ElectricalConnectionSuite) AfterTest(suiteName, testName string) { func (s *ElectricalConnectionSuite) TestDescriptionListData_RecvReply() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), ec_descriptionlistdata_recv_reply_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), ec_descriptionlistdata_recv_reply_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Assert @@ -75,7 +75,7 @@ func (s *ElectricalConnectionSuite) TestDescriptionListData_RecvReply() { func (s *ElectricalConnectionSuite) TestParameterDescriptionListData_RecvReply() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), ec_parameterdescriptionlistdata_recv_reply_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), ec_parameterdescriptionlistdata_recv_reply_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Assert @@ -111,7 +111,7 @@ func (s *ElectricalConnectionSuite) TestParameterDescriptionListData_RecvReply() func (s *ElectricalConnectionSuite) TestPermittedValueSetListData_RecvNotifyPartial() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), ec_permittedvaluesetlistdata_recv_notify_partial_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), ec_permittedvaluesetlistdata_recv_notify_partial_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Assert diff --git a/integration_tests/emobility_measurement_test.go b/integration_tests/emobility_measurement_test.go index 346b40b6..8cd25cba 100644 --- a/integration_tests/emobility_measurement_test.go +++ b/integration_tests/emobility_measurement_test.go @@ -17,9 +17,8 @@ func TestEmobilityMeasurementSuite(t *testing.T) { type EmobilityMeasurementSuite struct { suite.Suite - spine.SpineDataConnection - sut *spine.DeviceLocalImpl + sut spine.DeviceLocal localEntity spine.EntityLocal measurement *features.Measurement @@ -27,7 +26,7 @@ type EmobilityMeasurementSuite struct { remoteSki string - readHandler spine.SpineDataProcessing + remoteDevice spine.DeviceRemote writeHandler *WriteMessageHandler } @@ -35,6 +34,7 @@ func (s *EmobilityMeasurementSuite) SetupSuite() { } func (s *EmobilityMeasurementSuite) BeforeTest(suiteName, testName string) { + s.sut = spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) s.localEntity = spine.NewEntityLocalImpl(s.sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) @@ -48,9 +48,10 @@ func (s *EmobilityMeasurementSuite) BeforeTest(suiteName, testName string) { s.remoteSki = "TestRemoteSki" s.writeHandler = &WriteMessageHandler{} - s.readHandler = s.sut.AddRemoteDevice(s.remoteSki, s.writeHandler) + _ = s.sut.SetupRemoteDevice(s.remoteSki, s.writeHandler) + s.remoteDevice = s.sut.RemoteDeviceForSki(s.remoteSki) - initialCommunication(s.T(), s.readHandler, s.writeHandler) + initialCommunication(s.T(), s.remoteDevice, s.writeHandler) } func (s *EmobilityMeasurementSuite) AfterTest(suiteName, testName string) { @@ -68,13 +69,13 @@ func (s *EmobilityMeasurementSuite) TestGetValuesPerPhaseForScope() { assert.Nil(s.T(), err) // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), ec_parameterdescriptionlistdata_recv_reply_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), ec_parameterdescriptionlistdata_recv_reply_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) - msgCounter, _ = s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), m_descriptionListData_recv_reply_file_path)) + msgCounter, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_descriptionListData_recv_reply_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) - msgCounter, _ = s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), m_measurementListData_recv_notify_file_path)) + msgCounter, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_measurementListData_recv_notify_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) measurement := model.MeasurementTypeTypeCurrent diff --git a/integration_tests/helper_test.go b/integration_tests/helper_test.go index 21e57c38..7b486f64 100644 --- a/integration_tests/helper_test.go +++ b/integration_tests/helper_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" ) @@ -23,7 +24,7 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ spine.SpineDataConnection = (*WriteMessageHandler)(nil) +var _ ship.SpineDataConnection = (*WriteMessageHandler)(nil) func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { t.mux.Lock() @@ -95,7 +96,9 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg return nil } -func beforeTest(suiteName, testName string, fId uint, ftype model.FeatureTypeType, frole model.RoleType) (*spine.DeviceLocalImpl, string, spine.SpineDataProcessing, *WriteMessageHandler) { +func beforeTest( + suiteName, testName string, fId uint, ftype model.FeatureTypeType, + frole model.RoleType) (spine.DeviceLocal, string, spine.DeviceRemote, *WriteMessageHandler) { sut := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) @@ -106,18 +109,19 @@ func beforeTest(suiteName, testName string, fId uint, ftype model.FeatureTypeTyp remoteSki := "TestRemoteSki" writeHandler := &WriteMessageHandler{} - remoteDevice := sut.AddRemoteDevice(remoteSki, writeHandler) + _ = sut.SetupRemoteDevice(remoteSki, writeHandler) + remoteDevice := sut.RemoteDeviceForSki(remoteSki) return sut, remoteSki, remoteDevice, writeHandler } -func initialCommunication(t *testing.T, readHandler spine.SpineDataProcessing, writeHandler *WriteMessageHandler) { +func initialCommunication(t *testing.T, remoteDevice spine.DeviceRemote, writeHandler *WriteMessageHandler) { // Initial generic communication - _, _ = readHandler.HandleIncomingSpineMesssage(loadFileData(t, wallbox_detaileddiscoverydata_recv_reply_file_path)) + _, _ = remoteDevice.HandleSpineMesssage(loadFileData(t, wallbox_detaileddiscoverydata_recv_reply_file_path)) // Act - msgCounter, _ := readHandler.HandleIncomingSpineMesssage(loadFileData(t, wallbox_detaileddiscoverydata_recv_notify_file_path)) + msgCounter, _ := remoteDevice.HandleSpineMesssage(loadFileData(t, wallbox_detaileddiscoverydata_recv_notify_file_path)) waitForAck(t, msgCounter, writeHandler) } diff --git a/integration_tests/measurement_test.go b/integration_tests/measurement_test.go index aa143d07..f1a7967f 100644 --- a/integration_tests/measurement_test.go +++ b/integration_tests/measurement_test.go @@ -22,11 +22,11 @@ func TestMeasurementSuite(t *testing.T) { type MeasurementSuite struct { suite.Suite - sut *spine.DeviceLocalImpl + sut spine.DeviceLocal remoteSki string - readHandler spine.SpineDataProcessing + remoteDevice spine.DeviceRemote writeHandler *WriteMessageHandler } @@ -34,8 +34,8 @@ func (s *MeasurementSuite) SetupSuite() { } func (s *MeasurementSuite) BeforeTest(suiteName, testName string) { - s.sut, s.remoteSki, s.readHandler, s.writeHandler = beforeTest(suiteName, testName, 2, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - initialCommunication(s.T(), s.readHandler, s.writeHandler) + s.sut, s.remoteSki, s.remoteDevice, s.writeHandler = beforeTest(suiteName, testName, 2, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + initialCommunication(s.T(), s.remoteDevice, s.writeHandler) } func (s *MeasurementSuite) AfterTest(suiteName, testName string) { @@ -43,7 +43,7 @@ func (s *MeasurementSuite) AfterTest(suiteName, testName string) { func (s *MeasurementSuite) TestDescriptionList_Recv() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), m_descriptionListData_recv_reply_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_descriptionListData_recv_reply_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Assert @@ -76,7 +76,7 @@ func (s *MeasurementSuite) TestDescriptionList_Recv() { func (s *MeasurementSuite) TestMeasurementList_Recv() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), m_measurementListData_recv_notify_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_measurementListData_recv_notify_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Assert @@ -113,11 +113,11 @@ func (s *MeasurementSuite) TestMeasurementList_Recv() { func (s *MeasurementSuite) TestMeasurementByScope_Recv() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), m_descriptionListData_recv_reply_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_descriptionListData_recv_reply_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Act - msgCounter, _ = s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), m_measurementListData_recv_notify_file_path)) + msgCounter, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_measurementListData_recv_notify_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Assert diff --git a/service/hub.go b/service/hub.go index d64ac931..48e55615 100644 --- a/service/hub.go +++ b/service/hub.go @@ -65,8 +65,7 @@ type connectionsHubImpl struct { // list of currently known/reported mDNS entries knownMdnsEntries []*MdnsEntry - // the SPINE local device - spineLocalDevice spine.DeviceLocalConnection + spineLocalDevice spine.DeviceLocal muxCon sync.Mutex muxConAttempt sync.Mutex @@ -74,7 +73,7 @@ type connectionsHubImpl struct { muxMdns sync.Mutex } -func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice spine.DeviceLocalConnection, configuration *Configuration, localService *ServiceDetails) ConnectionsHub { +func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice spine.DeviceLocal, configuration *Configuration, localService *ServiceDetails) ConnectionsHub { hub := &connectionsHubImpl{ connections: make(map[string]ship.ShipConnection), connectionAttemptCounter: make(map[string]int), @@ -118,9 +117,14 @@ func (h *connectionsHubImpl) IsRemoteServiceForSKIPaired(ski string) bool { // The connection was closed, we need to clean up func (h *connectionsHubImpl) HandleConnectionClosed(connection ship.ShipConnection, handshakeCompleted bool) { + remoteSki := connection.RemoteSKI() + if h.spineLocalDevice != nil { + h.spineLocalDevice.RemoveRemoteDeviceConnection(remoteSki) + } + // only remove this connection if it is the registered one for the ski! // as we can have double connections but only one can be registered - if existingC := h.connectionForSKI(connection.RemoteSKI()); existingC != nil { + if existingC := h.connectionForSKI(remoteSki); existingC != nil { if existingC.DataHandler() == connection.DataHandler() { h.muxCon.Lock() delete(h.connections, connection.RemoteSKI()) @@ -144,6 +148,10 @@ func (h *connectionsHubImpl) HandleConnectionClosed(connection ship.ShipConnecti h.checkRestartMdnsSearch() } +func (h *connectionsHubImpl) SetupRemoteDevice(ski string, writeI ship.SpineDataConnection) ship.SpineDataProcessing { + return h.spineLocalDevice.SetupRemoteDevice(ski, writeI) +} + // return the number of paired services func (h *connectionsHubImpl) numberPairedServices() int { amount := 0 @@ -443,7 +451,7 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { } dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI) - shipConnection := ship.NewConnectionHandler(h, dataHandler, h.spineLocalDevice, ship.ShipRoleServer, + shipConnection := ship.NewConnectionHandler(h, dataHandler, ship.ShipRoleServer, h.localService.ShipID, remoteService.SKI, remoteService.ShipID) shipConnection.Run() @@ -508,7 +516,7 @@ func (h *connectionsHubImpl) connectFoundService(remoteService *ServiceDetails, } dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI) - shipConnection := ship.NewConnectionHandler(h, dataHandler, h.spineLocalDevice, ship.ShipRoleClient, + shipConnection := ship.NewConnectionHandler(h, dataHandler, ship.ShipRoleClient, h.localService.ShipID, remoteService.SKI, remoteService.ShipID) shipConnection.Run() diff --git a/service/hub_test.go b/service/hub_test.go index 250ea6a3..7c05ff60 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -36,7 +36,7 @@ type HubSuite struct { serviceProvider *MockServiceProvider mdnsService *MockMdnsService shipConnection *mocks.ShipConnection - shipDataConnection *mocks.ShipDataConnection + shipDataConnection *mocks.WebsocketDataConnection remoteSki string @@ -78,7 +78,7 @@ func (s *HubSuite) SetupSuite() { s.mdnsService.EXPECT().RegisterMdnsSearch(gomock.Any()).AnyTimes() s.mdnsService.EXPECT().UnregisterMdnsSearch(gomock.Any()).AnyTimes() - s.shipDataConnection = mocks.NewShipDataConnection(s.T()) + s.shipDataConnection = mocks.NewWebsocketDataConnection(s.T()) s.shipConnection = mocks.NewShipConnection(s.T()) s.shipConnection.On("CloseConnection", mock.Anything, mock.Anything, mock.Anything).Return().Maybe() diff --git a/ship/api.go b/ship/api.go index 48b15c4a..ea2ec108 100644 --- a/ship/api.go +++ b/ship/api.go @@ -1,11 +1,11 @@ package ship -//go:generate mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider -//go:generate mockery --name=ShipDataConnection +//go:generate mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship WebsocketDataConnection,WebsocketDataProcessing,ShipServiceDataProvider,SpineDataProcessing,SpineDataConnection +//go:generate mockery --name=WebsocketDataConnection //go:generate mockery --name=ShipConnection type ShipConnection interface { - DataHandler() ShipDataConnection + DataHandler() WebsocketDataConnection CloseConnection(safe bool, code int, reason string) RemoteSKI() string ApprovePendingHandshake() @@ -13,12 +13,49 @@ type ShipConnection interface { ShipHandshakeState() (ShipMessageExchangeState, error) } +// interface for getting service wide information +// +// implemented by connectionsHub, used by shipConnection +type ShipServiceDataProvider interface { + // check if the SKI is paired + IsRemoteServiceForSKIPaired(string) bool + + // report closing of a connection and if handshake did complete + HandleConnectionClosed(ShipConnection, bool) + + // report the ship ID provided during the handshake + ReportServiceShipID(string, string) + + // check if the user is still able to trust the connection + AllowWaitingForTrust(string) bool + + // report the updated SHIP handshake state and optional error message for a SKI + HandleShipHandshakeStateUpdate(string, ShipState) + + // report an approved handshake by a remote device + SetupRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing +} + +// Used to pass an outgoing SPINE message from a DeviceLocal to the SHIP connection +// +// Implemented by ShipConnection, used by spine DeviceLocal +type SpineDataConnection interface { + WriteSpineMessage(message []byte) +} + +// Used to pass an incoming SPINE message from a SHIP connection to the proper DeviceRemote +// +// Implemented by spine DeviceRemote, used by ShipConnection +type SpineDataProcessing interface { + HandleIncomingSpineMesssage(message []byte) +} + // interface for handling the actual remote device data connection // // implemented by websocketConnection, used by ShipConnection -type ShipDataConnection interface { +type WebsocketDataConnection interface { // initialize data processing - InitDataProcessing(ShipDataProcessing) + InitDataProcessing(WebsocketDataProcessing) // send data via the connection to the remote device WriteMessageToDataConnection([]byte) error @@ -33,7 +70,7 @@ type ShipDataConnection interface { // interface for handling incoming data // // implemented by shipConnection, used by websocketConnection -type ShipDataProcessing interface { +type WebsocketDataProcessing interface { // called for each incoming message HandleIncomingShipMessage([]byte) @@ -41,23 +78,3 @@ type ShipDataProcessing interface { // e.g. due to connection issues ReportConnectionError(error) } - -// interface for getting service wide information -// -// implemented by connectionsHub, used by shipConnection -type ShipServiceDataProvider interface { - // check if the SKI is paired - IsRemoteServiceForSKIPaired(string) bool - - // report closing of a connection and if handshake did complete - HandleConnectionClosed(ShipConnection, bool) - - // report the ship ID provided during the handshake - ReportServiceShipID(string, string) - - // check if the user is still able to trust the connection - AllowWaitingForTrust(string) bool - - // report the updated SHIP handshake state and optional error message for a SKI - HandleShipHandshakeStateUpdate(string, ShipState) -} diff --git a/ship/connection.go b/ship/connection.go index 0a4e8c71..7d44df56 100644 --- a/ship/connection.go +++ b/ship/connection.go @@ -11,7 +11,6 @@ import ( "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/ship/model" shipUtil "github.com/enbility/eebus-go/ship/util" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/util" ) @@ -33,10 +32,10 @@ type ShipConnectionImpl struct { serviceDataProvider ShipServiceDataProvider // Where to pass incoming SPINE messages to - spineDataProcessing spine.SpineDataProcessing + spineDataProcessing SpineDataProcessing - // the handler for sending messages on the data connection - dataHandler ShipDataConnection + // the (web socket) handler for sending messages + dataHandler WebsocketDataConnection // The current SHIP state smeState ShipMessageExchangeState @@ -58,9 +57,6 @@ type ShipConnectionImpl struct { lastReceivedWaitingValue time.Duration // required for Prolong-Request-Reply-Timer - // the SPINE local device - deviceLocalCon spine.DeviceLocalConnection - shutdownOnce sync.Once mux sync.Mutex @@ -68,15 +64,20 @@ type ShipConnectionImpl struct { var _ ShipConnection = (*ShipConnectionImpl)(nil) -func NewConnectionHandler(dataProvider ShipServiceDataProvider, dataHandler ShipDataConnection, deviceLocalCon spine.DeviceLocalConnection, role shipRole, localShipID, remoteSki, remoteShipId string) *ShipConnectionImpl { +func NewConnectionHandler( + dataProvider ShipServiceDataProvider, + dataHandler WebsocketDataConnection, + role shipRole, + localShipID, + remoteSki, + remoteShipId string) *ShipConnectionImpl { ship := &ShipConnectionImpl{ serviceDataProvider: dataProvider, - deviceLocalCon: deviceLocalCon, + dataHandler: dataHandler, role: role, localShipID: localShipID, remoteSKI: remoteSki, remoteShipID: remoteShipId, - dataHandler: dataHandler, smeState: CmiStateInitStart, smeError: nil, } @@ -94,7 +95,7 @@ func (c *ShipConnectionImpl) RemoteSKI() string { return c.remoteSKI } -func (c *ShipConnectionImpl) DataHandler() ShipDataConnection { +func (c *ShipConnectionImpl) DataHandler() WebsocketDataConnection { return c.dataHandler } @@ -142,21 +143,11 @@ func (c *ShipConnectionImpl) AbortPendingHandshake() { c.setAndHandleState(SmeHelloStateAbort) } -// report removing a connection -func (c *ShipConnectionImpl) removeRemoteDeviceConnection() { - if c.deviceLocalCon == nil { - return - } - c.deviceLocalCon.RemoveRemoteDeviceConnection(c.remoteSKI) -} - // close this ship connection func (c *ShipConnectionImpl) CloseConnection(safe bool, code int, reason string) { c.shutdownOnce.Do(func() { c.stopHandshakeTimer() - c.removeRemoteDeviceConnection() - // handshake is completed if approved or aborted state := c.getState() handshakeEnd := state == SmeStateComplete || @@ -192,7 +183,7 @@ func (c *ShipConnectionImpl) CloseConnection(safe bool, code int, reason string) }) } -var _ spine.SpineDataConnection = (*ShipConnectionImpl)(nil) +var _ SpineDataConnection = (*ShipConnectionImpl)(nil) // SpineDataConnection interface implementation func (c *ShipConnectionImpl) WriteSpineMessage(message []byte) { @@ -202,7 +193,7 @@ func (c *ShipConnectionImpl) WriteSpineMessage(message []byte) { } } -var _ ShipDataProcessing = (*ShipConnectionImpl)(nil) +var _ WebsocketDataProcessing = (*ShipConnectionImpl)(nil) func (c *ShipConnectionImpl) shipModelFromMessage(message []byte) (*model.ShipData, error) { _, jsonData := c.parseMessage(message, true) @@ -241,7 +232,7 @@ func (c *ShipConnectionImpl) HandleIncomingShipMessage(message []byte) { } // pass the payload to the SPINE read handler - _, _ = c.spineDataProcessing.HandleIncomingSpineMesssage([]byte(data.Data.Payload)) + c.spineDataProcessing.HandleIncomingSpineMesssage([]byte(data.Data.Payload)) } // checks wether the provided messages is a SHIP message diff --git a/ship/connection_test.go b/ship/connection_test.go index dbb7227a..8ef3bb63 100644 --- a/ship/connection_test.go +++ b/ship/connection_test.go @@ -3,14 +3,9 @@ package ship import ( "encoding/json" "testing" - "time" "github.com/enbility/eebus-go/ship/model" - "github.com/enbility/eebus-go/spine" - spineMocks "github.com/enbility/eebus-go/spine/mocks" - spineModel "github.com/enbility/eebus-go/spine/model" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" ) @@ -25,41 +20,39 @@ type ConnectionSuite struct { sut *ShipConnectionImpl shipDataProvider *MockShipServiceDataProvider - shipDataConn *MockShipDataConnection + shipDataConn *MockWebsocketDataConnection - spineDataProcessing *spineMocks.SpineDataProcessing + spineDataProcessing *MockSpineDataProcessing sentMessage []byte } -func (s *ConnectionSuite) SetupSuite() {} -func (s *ConnectionSuite) TearDownTest() {} - func (s *ConnectionSuite) BeforeTest(suiteName, testName string) { s.sentMessage = nil - localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart, time.Second*4) ctrl := gomock.NewController(s.T()) s.shipDataProvider = NewMockShipServiceDataProvider(ctrl) s.shipDataProvider.EXPECT().HandleShipHandshakeStateUpdate(gomock.Any(), gomock.Any()).AnyTimes() s.shipDataProvider.EXPECT().HandleConnectionClosed(gomock.Any(), gomock.Any()).AnyTimes() + s.shipDataProvider.EXPECT().IsRemoteServiceForSKIPaired(gomock.Any()).AnyTimes() + s.shipDataProvider.EXPECT().AllowWaitingForTrust(gomock.Any()).AnyTimes() - s.shipDataConn = NewMockShipDataConnection(ctrl) + s.shipDataConn = NewMockWebsocketDataConnection(ctrl) s.shipDataConn.EXPECT().InitDataProcessing(gomock.Any()).AnyTimes() s.shipDataConn.EXPECT().WriteMessageToDataConnection(gomock.Any()).DoAndReturn(func(message []byte) error { s.sentMessage = message; return nil }).AnyTimes() s.shipDataConn.EXPECT().IsDataConnectionClosed().DoAndReturn(func() (bool, error) { return false, nil }).AnyTimes() s.shipDataConn.EXPECT().CloseDataConnection(gomock.Any(), gomock.Any()).AnyTimes() - s.spineDataProcessing = spineMocks.NewSpineDataProcessing(s.T()) + s.spineDataProcessing = NewMockSpineDataProcessing(ctrl) + s.spineDataProcessing.EXPECT().HandleIncomingSpineMesssage(gomock.Any()).AnyTimes() - s.sut = NewConnectionHandler(s.shipDataProvider, s.shipDataConn, localDevice, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID") + s.sut = NewConnectionHandler(s.shipDataProvider, s.shipDataConn, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID") } func (s *ConnectionSuite) Test_RemoteSKI() { remoteSki := s.sut.RemoteSKI() - assert.Equal(s.T(), s.sut.remoteSKI, remoteSki) + assert.NotEqual(s.T(), "", remoteSki) } func (s *ConnectionSuite) Test_DataHandler() { @@ -69,7 +62,9 @@ func (s *ConnectionSuite) Test_DataHandler() { func (s *ConnectionSuite) TestRun() { s.sut.Run() - assert.Equal(s.T(), CmiStateServerWait, s.sut.smeState) + state, err := s.sut.ShipHandshakeState() + assert.Nil(s.T(), err) + assert.Equal(s.T(), CmiStateServerWait, state) } func (s *ConnectionSuite) TestShipHandshakeState() { @@ -98,14 +93,6 @@ func (s *ConnectionSuite) TestAbortPendingHandshake() { assert.Equal(s.T(), SmeHelloStateAbortDone, s.sut.smeState) } -func (s *ConnectionSuite) TestRemoveRemoteDeviceConnection() { - s.sut.removeRemoteDeviceConnection() - - s.sut.deviceLocalCon = nil - - s.sut.removeRemoteDeviceConnection() -} - func (s *ConnectionSuite) TestCloseConnection_StateComplete() { s.sut.smeState = SmeStateComplete s.sut.CloseConnection(true, 450, "User Close") @@ -157,15 +144,12 @@ func (s *ConnectionSuite) TestHandleIncomingShipMessage() { s.sut.HandleIncomingShipMessage(msg) - spineData := spineModel.Datagram{} - jsonData, err = json.Marshal(spineData) - assert.Nil(s.T(), err) + spineData := `{"datagram":{}}` + jsonData = []byte(spineData) - rawBytes := []byte{} - rawBytes = append(rawBytes, jsonData...) modelData = model.ShipData{ Data: model.DataType{ - Payload: rawBytes, + Payload: jsonData, }, } jsonData, err = json.Marshal(modelData) @@ -176,7 +160,6 @@ func (s *ConnectionSuite) TestHandleIncomingShipMessage() { s.sut.HandleIncomingShipMessage(msg) - s.spineDataProcessing.On("HandleIncomingSpineMesssage", mock.Anything).Return(nil, nil) s.sut.spineDataProcessing = s.spineDataProcessing s.sut.HandleIncomingShipMessage(msg) @@ -232,18 +215,8 @@ func (s *ConnectionSuite) TestProcessShipJsonMessage() { } func (s *ConnectionSuite) TestSendSpineMessage() { - data := spineModel.Datagram{ - Datagram: spineModel.DatagramType{ - Header: spineModel.HeaderType{}, - Payload: spineModel.PayloadType{ - Cmd: []spineModel.CmdType{}, - }, - }, - } - - msg, err := json.Marshal(data) - assert.Nil(s.T(), err) + data := `{"datagram":{"header":{},"payload":{"cmd":[]}}}` - err = s.sut.sendSpineData(msg) + err := s.sut.sendSpineData([]byte(data)) assert.Nil(s.T(), err) } diff --git a/ship/handshake.go b/ship/handshake.go index b2db2773..86b4b0d1 100644 --- a/ship/handshake.go +++ b/ship/handshake.go @@ -199,7 +199,7 @@ func (c *ShipConnectionImpl) setAndHandleState(state ShipMessageExchangeState) { // SHIP handshake is approved, now set the new state and the SPINE read handler func (c *ShipConnectionImpl) approveHandshake() { // Report to SPINE local device about this remote device connection - c.spineDataProcessing = c.deviceLocalCon.AddRemoteDevice(c.remoteSKI, c) + c.spineDataProcessing = c.serviceDataProvider.SetupRemoteDevice(c.remoteSKI, c) c.stopHandshakeTimer() c.setState(SmeStateComplete, nil) } diff --git a/ship/hs_access_test.go b/ship/hs_access_test.go index e8b4be3b..53f4f148 100644 --- a/ship/hs_access_test.go +++ b/ship/hs_access_test.go @@ -70,7 +70,7 @@ func (s *AccessSuite) Test_Request_Invalid() { } func (s *AccessSuite) Test_Methods_Ok() { - sut, data := initTest(ShipRoleClient) + sut, _ := initTest(ShipRoleClient) sut.setState(SmeAccessMethodsRequest, nil) @@ -87,7 +87,6 @@ func (s *AccessSuite) Test_Methods_Ok() { assert.Equal(s.T(), false, sut.handshakeTimerRunning) assert.Equal(s.T(), SmeStateComplete, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) shutdownTest(sut) } diff --git a/ship/hs_helper_test.go b/ship/hs_helper_test.go index 859a7011..e72ca97a 100644 --- a/ship/hs_helper_test.go +++ b/ship/hs_helper_test.go @@ -2,10 +2,6 @@ package ship import ( "sync" - "time" - - "github.com/enbility/eebus-go/spine" - spineModel "github.com/enbility/eebus-go/spine/model" ) type dataHandlerTest struct { @@ -25,9 +21,9 @@ func (s *dataHandlerTest) lastMessage() []byte { return s.sentMessage } -var _ ShipDataConnection = (*dataHandlerTest)(nil) +var _ WebsocketDataConnection = (*dataHandlerTest)(nil) -func (s *dataHandlerTest) InitDataProcessing(dataProcessing ShipDataProcessing) {} +func (s *dataHandlerTest) InitDataProcessing(dataProcessing WebsocketDataProcessing) {} func (s *dataHandlerTest) WriteMessageToDataConnection(message []byte) error { s.mux.Lock() @@ -40,6 +36,9 @@ func (s *dataHandlerTest) WriteMessageToDataConnection(message []byte) error { func (s *dataHandlerTest) CloseDataConnection(int, string) {} func (w *dataHandlerTest) IsDataConnectionClosed() (bool, error) { return false, nil } +func (w *dataHandlerTest) SetupRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing { + return nil +} var _ ShipServiceDataProvider = (*dataHandlerTest)(nil) @@ -54,11 +53,8 @@ func (s *dataHandlerTest) AllowWaitingForTrust(string) bool { func (s *dataHandlerTest) HandleShipHandshakeStateUpdate(string, ShipState) {} func initTest(role shipRole) (*ShipConnectionImpl, *dataHandlerTest) { - localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", spineModel.DeviceTypeTypeEnergyManagementSystem, spineModel.NetworkManagementFeatureSetTypeSmart, time.Second*4) - dataHandler := &dataHandlerTest{} - conhandler := NewConnectionHandler(dataHandler, dataHandler, localDevice, role, "LocalShipID", "RemoveDevice", "RemoteShipID") + conhandler := NewConnectionHandler(dataHandler, dataHandler, role, "LocalShipID", "RemoveDevice", "RemoteShipID") return conhandler, dataHandler } diff --git a/ship/mock_types_test.go b/ship/mock_types_test.go index b19b32da..e1abe9d3 100644 --- a/ship/mock_types_test.go +++ b/ship/mock_types_test.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/enbility/eebus-go/ship (interfaces: ShipDataConnection,ShipDataProcessing,ShipServiceDataProvider) +// Source: github.com/enbility/eebus-go/ship (interfaces: WebsocketDataConnection,WebsocketDataProcessing,ShipServiceDataProvider,SpineDataProcessing,SpineDataConnection) +// +// Generated by this command: +// +// mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship WebsocketDataConnection,WebsocketDataProcessing,ShipServiceDataProvider,SpineDataProcessing,SpineDataConnection +// // Package ship is a generated GoMock package. package ship @@ -10,55 +15,55 @@ import ( gomock "go.uber.org/mock/gomock" ) -// MockShipDataConnection is a mock of ShipDataConnection interface. -type MockShipDataConnection struct { +// MockWebsocketDataConnection is a mock of WebsocketDataConnection interface. +type MockWebsocketDataConnection struct { ctrl *gomock.Controller - recorder *MockShipDataConnectionMockRecorder + recorder *MockWebsocketDataConnectionMockRecorder } -// MockShipDataConnectionMockRecorder is the mock recorder for MockShipDataConnection. -type MockShipDataConnectionMockRecorder struct { - mock *MockShipDataConnection +// MockWebsocketDataConnectionMockRecorder is the mock recorder for MockWebsocketDataConnection. +type MockWebsocketDataConnectionMockRecorder struct { + mock *MockWebsocketDataConnection } -// NewMockShipDataConnection creates a new mock instance. -func NewMockShipDataConnection(ctrl *gomock.Controller) *MockShipDataConnection { - mock := &MockShipDataConnection{ctrl: ctrl} - mock.recorder = &MockShipDataConnectionMockRecorder{mock} +// NewMockWebsocketDataConnection creates a new mock instance. +func NewMockWebsocketDataConnection(ctrl *gomock.Controller) *MockWebsocketDataConnection { + mock := &MockWebsocketDataConnection{ctrl: ctrl} + mock.recorder = &MockWebsocketDataConnectionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockShipDataConnection) EXPECT() *MockShipDataConnectionMockRecorder { +func (m *MockWebsocketDataConnection) EXPECT() *MockWebsocketDataConnectionMockRecorder { return m.recorder } // CloseDataConnection mocks base method. -func (m *MockShipDataConnection) CloseDataConnection(arg0 int, arg1 string) { +func (m *MockWebsocketDataConnection) CloseDataConnection(arg0 int, arg1 string) { m.ctrl.T.Helper() m.ctrl.Call(m, "CloseDataConnection", arg0, arg1) } // CloseDataConnection indicates an expected call of CloseDataConnection. -func (mr *MockShipDataConnectionMockRecorder) CloseDataConnection(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockWebsocketDataConnectionMockRecorder) CloseDataConnection(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseDataConnection", reflect.TypeOf((*MockShipDataConnection)(nil).CloseDataConnection), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseDataConnection", reflect.TypeOf((*MockWebsocketDataConnection)(nil).CloseDataConnection), arg0, arg1) } // InitDataProcessing mocks base method. -func (m *MockShipDataConnection) InitDataProcessing(arg0 ShipDataProcessing) { +func (m *MockWebsocketDataConnection) InitDataProcessing(arg0 WebsocketDataProcessing) { m.ctrl.T.Helper() m.ctrl.Call(m, "InitDataProcessing", arg0) } // InitDataProcessing indicates an expected call of InitDataProcessing. -func (mr *MockShipDataConnectionMockRecorder) InitDataProcessing(arg0 interface{}) *gomock.Call { +func (mr *MockWebsocketDataConnectionMockRecorder) InitDataProcessing(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitDataProcessing", reflect.TypeOf((*MockShipDataConnection)(nil).InitDataProcessing), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitDataProcessing", reflect.TypeOf((*MockWebsocketDataConnection)(nil).InitDataProcessing), arg0) } // IsDataConnectionClosed mocks base method. -func (m *MockShipDataConnection) IsDataConnectionClosed() (bool, error) { +func (m *MockWebsocketDataConnection) IsDataConnectionClosed() (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDataConnectionClosed") ret0, _ := ret[0].(bool) @@ -67,13 +72,13 @@ func (m *MockShipDataConnection) IsDataConnectionClosed() (bool, error) { } // IsDataConnectionClosed indicates an expected call of IsDataConnectionClosed. -func (mr *MockShipDataConnectionMockRecorder) IsDataConnectionClosed() *gomock.Call { +func (mr *MockWebsocketDataConnectionMockRecorder) IsDataConnectionClosed() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDataConnectionClosed", reflect.TypeOf((*MockShipDataConnection)(nil).IsDataConnectionClosed)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDataConnectionClosed", reflect.TypeOf((*MockWebsocketDataConnection)(nil).IsDataConnectionClosed)) } // WriteMessageToDataConnection mocks base method. -func (m *MockShipDataConnection) WriteMessageToDataConnection(arg0 []byte) error { +func (m *MockWebsocketDataConnection) WriteMessageToDataConnection(arg0 []byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteMessageToDataConnection", arg0) ret0, _ := ret[0].(error) @@ -81,56 +86,56 @@ func (m *MockShipDataConnection) WriteMessageToDataConnection(arg0 []byte) error } // WriteMessageToDataConnection indicates an expected call of WriteMessageToDataConnection. -func (mr *MockShipDataConnectionMockRecorder) WriteMessageToDataConnection(arg0 interface{}) *gomock.Call { +func (mr *MockWebsocketDataConnectionMockRecorder) WriteMessageToDataConnection(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteMessageToDataConnection", reflect.TypeOf((*MockShipDataConnection)(nil).WriteMessageToDataConnection), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteMessageToDataConnection", reflect.TypeOf((*MockWebsocketDataConnection)(nil).WriteMessageToDataConnection), arg0) } -// MockShipDataProcessing is a mock of ShipDataProcessing interface. -type MockShipDataProcessing struct { +// MockWebsocketDataProcessing is a mock of WebsocketDataProcessing interface. +type MockWebsocketDataProcessing struct { ctrl *gomock.Controller - recorder *MockShipDataProcessingMockRecorder + recorder *MockWebsocketDataProcessingMockRecorder } -// MockShipDataProcessingMockRecorder is the mock recorder for MockShipDataProcessing. -type MockShipDataProcessingMockRecorder struct { - mock *MockShipDataProcessing +// MockWebsocketDataProcessingMockRecorder is the mock recorder for MockWebsocketDataProcessing. +type MockWebsocketDataProcessingMockRecorder struct { + mock *MockWebsocketDataProcessing } -// NewMockShipDataProcessing creates a new mock instance. -func NewMockShipDataProcessing(ctrl *gomock.Controller) *MockShipDataProcessing { - mock := &MockShipDataProcessing{ctrl: ctrl} - mock.recorder = &MockShipDataProcessingMockRecorder{mock} +// NewMockWebsocketDataProcessing creates a new mock instance. +func NewMockWebsocketDataProcessing(ctrl *gomock.Controller) *MockWebsocketDataProcessing { + mock := &MockWebsocketDataProcessing{ctrl: ctrl} + mock.recorder = &MockWebsocketDataProcessingMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockShipDataProcessing) EXPECT() *MockShipDataProcessingMockRecorder { +func (m *MockWebsocketDataProcessing) EXPECT() *MockWebsocketDataProcessingMockRecorder { return m.recorder } // HandleIncomingShipMessage mocks base method. -func (m *MockShipDataProcessing) HandleIncomingShipMessage(arg0 []byte) { +func (m *MockWebsocketDataProcessing) HandleIncomingShipMessage(arg0 []byte) { m.ctrl.T.Helper() m.ctrl.Call(m, "HandleIncomingShipMessage", arg0) } // HandleIncomingShipMessage indicates an expected call of HandleIncomingShipMessage. -func (mr *MockShipDataProcessingMockRecorder) HandleIncomingShipMessage(arg0 interface{}) *gomock.Call { +func (mr *MockWebsocketDataProcessingMockRecorder) HandleIncomingShipMessage(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleIncomingShipMessage", reflect.TypeOf((*MockShipDataProcessing)(nil).HandleIncomingShipMessage), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleIncomingShipMessage", reflect.TypeOf((*MockWebsocketDataProcessing)(nil).HandleIncomingShipMessage), arg0) } // ReportConnectionError mocks base method. -func (m *MockShipDataProcessing) ReportConnectionError(arg0 error) { +func (m *MockWebsocketDataProcessing) ReportConnectionError(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportConnectionError", arg0) } // ReportConnectionError indicates an expected call of ReportConnectionError. -func (mr *MockShipDataProcessingMockRecorder) ReportConnectionError(arg0 interface{}) *gomock.Call { +func (mr *MockWebsocketDataProcessingMockRecorder) ReportConnectionError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportConnectionError", reflect.TypeOf((*MockShipDataProcessing)(nil).ReportConnectionError), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportConnectionError", reflect.TypeOf((*MockWebsocketDataProcessing)(nil).ReportConnectionError), arg0) } // MockShipServiceDataProvider is a mock of ShipServiceDataProvider interface. @@ -165,7 +170,7 @@ func (m *MockShipServiceDataProvider) AllowWaitingForTrust(arg0 string) bool { } // AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. -func (mr *MockShipServiceDataProviderMockRecorder) AllowWaitingForTrust(arg0 interface{}) *gomock.Call { +func (mr *MockShipServiceDataProviderMockRecorder) AllowWaitingForTrust(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockShipServiceDataProvider)(nil).AllowWaitingForTrust), arg0) } @@ -177,7 +182,7 @@ func (m *MockShipServiceDataProvider) HandleConnectionClosed(arg0 ShipConnection } // HandleConnectionClosed indicates an expected call of HandleConnectionClosed. -func (mr *MockShipServiceDataProviderMockRecorder) HandleConnectionClosed(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockShipServiceDataProviderMockRecorder) HandleConnectionClosed(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleConnectionClosed", reflect.TypeOf((*MockShipServiceDataProvider)(nil).HandleConnectionClosed), arg0, arg1) } @@ -189,7 +194,7 @@ func (m *MockShipServiceDataProvider) HandleShipHandshakeStateUpdate(arg0 string } // HandleShipHandshakeStateUpdate indicates an expected call of HandleShipHandshakeStateUpdate. -func (mr *MockShipServiceDataProviderMockRecorder) HandleShipHandshakeStateUpdate(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockShipServiceDataProviderMockRecorder) HandleShipHandshakeStateUpdate(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleShipHandshakeStateUpdate", reflect.TypeOf((*MockShipServiceDataProvider)(nil).HandleShipHandshakeStateUpdate), arg0, arg1) } @@ -203,7 +208,7 @@ func (m *MockShipServiceDataProvider) IsRemoteServiceForSKIPaired(arg0 string) b } // IsRemoteServiceForSKIPaired indicates an expected call of IsRemoteServiceForSKIPaired. -func (mr *MockShipServiceDataProviderMockRecorder) IsRemoteServiceForSKIPaired(arg0 interface{}) *gomock.Call { +func (mr *MockShipServiceDataProviderMockRecorder) IsRemoteServiceForSKIPaired(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsRemoteServiceForSKIPaired", reflect.TypeOf((*MockShipServiceDataProvider)(nil).IsRemoteServiceForSKIPaired), arg0) } @@ -215,7 +220,91 @@ func (m *MockShipServiceDataProvider) ReportServiceShipID(arg0, arg1 string) { } // ReportServiceShipID indicates an expected call of ReportServiceShipID. -func (mr *MockShipServiceDataProviderMockRecorder) ReportServiceShipID(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockShipServiceDataProviderMockRecorder) ReportServiceShipID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportServiceShipID", reflect.TypeOf((*MockShipServiceDataProvider)(nil).ReportServiceShipID), arg0, arg1) } + +// SetupRemoteDevice mocks base method. +func (m *MockShipServiceDataProvider) SetupRemoteDevice(arg0 string, arg1 SpineDataConnection) SpineDataProcessing { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetupRemoteDevice", arg0, arg1) + ret0, _ := ret[0].(SpineDataProcessing) + return ret0 +} + +// SetupRemoteDevice indicates an expected call of SetupRemoteDevice. +func (mr *MockShipServiceDataProviderMockRecorder) SetupRemoteDevice(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupRemoteDevice", reflect.TypeOf((*MockShipServiceDataProvider)(nil).SetupRemoteDevice), arg0, arg1) +} + +// MockSpineDataProcessing is a mock of SpineDataProcessing interface. +type MockSpineDataProcessing struct { + ctrl *gomock.Controller + recorder *MockSpineDataProcessingMockRecorder +} + +// MockSpineDataProcessingMockRecorder is the mock recorder for MockSpineDataProcessing. +type MockSpineDataProcessingMockRecorder struct { + mock *MockSpineDataProcessing +} + +// NewMockSpineDataProcessing creates a new mock instance. +func NewMockSpineDataProcessing(ctrl *gomock.Controller) *MockSpineDataProcessing { + mock := &MockSpineDataProcessing{ctrl: ctrl} + mock.recorder = &MockSpineDataProcessingMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSpineDataProcessing) EXPECT() *MockSpineDataProcessingMockRecorder { + return m.recorder +} + +// HandleIncomingSpineMesssage mocks base method. +func (m *MockSpineDataProcessing) HandleIncomingSpineMesssage(arg0 []byte) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HandleIncomingSpineMesssage", arg0) +} + +// HandleIncomingSpineMesssage indicates an expected call of HandleIncomingSpineMesssage. +func (mr *MockSpineDataProcessingMockRecorder) HandleIncomingSpineMesssage(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleIncomingSpineMesssage", reflect.TypeOf((*MockSpineDataProcessing)(nil).HandleIncomingSpineMesssage), arg0) +} + +// MockSpineDataConnection is a mock of SpineDataConnection interface. +type MockSpineDataConnection struct { + ctrl *gomock.Controller + recorder *MockSpineDataConnectionMockRecorder +} + +// MockSpineDataConnectionMockRecorder is the mock recorder for MockSpineDataConnection. +type MockSpineDataConnectionMockRecorder struct { + mock *MockSpineDataConnection +} + +// NewMockSpineDataConnection creates a new mock instance. +func NewMockSpineDataConnection(ctrl *gomock.Controller) *MockSpineDataConnection { + mock := &MockSpineDataConnection{ctrl: ctrl} + mock.recorder = &MockSpineDataConnectionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSpineDataConnection) EXPECT() *MockSpineDataConnectionMockRecorder { + return m.recorder +} + +// WriteSpineMessage mocks base method. +func (m *MockSpineDataConnection) WriteSpineMessage(arg0 []byte) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "WriteSpineMessage", arg0) +} + +// WriteSpineMessage indicates an expected call of WriteSpineMessage. +func (mr *MockSpineDataConnectionMockRecorder) WriteSpineMessage(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteSpineMessage", reflect.TypeOf((*MockSpineDataConnection)(nil).WriteSpineMessage), arg0) +} diff --git a/ship/mocks/ShipConnection.go b/ship/mocks/ShipConnection.go index f9b0f582..afd5fc3a 100644 --- a/ship/mocks/ShipConnection.go +++ b/ship/mocks/ShipConnection.go @@ -28,19 +28,19 @@ func (_m *ShipConnection) CloseConnection(safe bool, code int, reason string) { } // DataHandler provides a mock function with given fields: -func (_m *ShipConnection) DataHandler() ship.ShipDataConnection { +func (_m *ShipConnection) DataHandler() ship.WebsocketDataConnection { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for DataHandler") } - var r0 ship.ShipDataConnection - if rf, ok := ret.Get(0).(func() ship.ShipDataConnection); ok { + var r0 ship.WebsocketDataConnection + if rf, ok := ret.Get(0).(func() ship.WebsocketDataConnection); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(ship.ShipDataConnection) + r0 = ret.Get(0).(ship.WebsocketDataConnection) } } diff --git a/ship/mocks/ShipDataConnection.go b/ship/mocks/WebsocketDataConnection.go similarity index 63% rename from ship/mocks/ShipDataConnection.go rename to ship/mocks/WebsocketDataConnection.go index 645072ce..f796e13b 100644 --- a/ship/mocks/ShipDataConnection.go +++ b/ship/mocks/WebsocketDataConnection.go @@ -7,23 +7,23 @@ import ( mock "github.com/stretchr/testify/mock" ) -// ShipDataConnection is an autogenerated mock type for the ShipDataConnection type -type ShipDataConnection struct { +// WebsocketDataConnection is an autogenerated mock type for the WebsocketDataConnection type +type WebsocketDataConnection struct { mock.Mock } // CloseDataConnection provides a mock function with given fields: closeCode, reason -func (_m *ShipDataConnection) CloseDataConnection(closeCode int, reason string) { +func (_m *WebsocketDataConnection) CloseDataConnection(closeCode int, reason string) { _m.Called(closeCode, reason) } // InitDataProcessing provides a mock function with given fields: _a0 -func (_m *ShipDataConnection) InitDataProcessing(_a0 ship.ShipDataProcessing) { +func (_m *WebsocketDataConnection) InitDataProcessing(_a0 ship.WebsocketDataProcessing) { _m.Called(_a0) } // IsDataConnectionClosed provides a mock function with given fields: -func (_m *ShipDataConnection) IsDataConnectionClosed() (bool, error) { +func (_m *WebsocketDataConnection) IsDataConnectionClosed() (bool, error) { ret := _m.Called() if len(ret) == 0 { @@ -51,7 +51,7 @@ func (_m *ShipDataConnection) IsDataConnectionClosed() (bool, error) { } // WriteMessageToDataConnection provides a mock function with given fields: _a0 -func (_m *ShipDataConnection) WriteMessageToDataConnection(_a0 []byte) error { +func (_m *WebsocketDataConnection) WriteMessageToDataConnection(_a0 []byte) error { ret := _m.Called(_a0) if len(ret) == 0 { @@ -68,13 +68,13 @@ func (_m *ShipDataConnection) WriteMessageToDataConnection(_a0 []byte) error { return r0 } -// NewShipDataConnection creates a new instance of ShipDataConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// NewWebsocketDataConnection creates a new instance of WebsocketDataConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func NewShipDataConnection(t interface { +func NewWebsocketDataConnection(t interface { mock.TestingT Cleanup(func()) -}) *ShipDataConnection { - mock := &ShipDataConnection{} +}) *WebsocketDataConnection { + mock := &WebsocketDataConnection{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/ship/websocket.go b/ship/websocket.go index 7f11c07d..61366559 100644 --- a/ship/websocket.go +++ b/ship/websocket.go @@ -18,7 +18,7 @@ type websocketConnection struct { conn *websocket.Conn // The implementation handling message processing - dataProcessing ShipDataProcessing + dataProcessing WebsocketDataProcessing // The connection was closed closeChannel chan struct{} @@ -246,9 +246,9 @@ func (w *websocketConnection) close() { }) } -var _ ShipDataConnection = (*websocketConnection)(nil) +var _ WebsocketDataConnection = (*websocketConnection)(nil) -func (w *websocketConnection) InitDataProcessing(dataProcessing ShipDataProcessing) { +func (w *websocketConnection) InitDataProcessing(dataProcessing WebsocketDataProcessing) { w.dataProcessing = dataProcessing w.run() diff --git a/ship/websocket_test.go b/ship/websocket_test.go index 71a6051f..86f70ef2 100644 --- a/ship/websocket_test.go +++ b/ship/websocket_test.go @@ -28,7 +28,7 @@ type WebsocketSuite struct { testServer *httptest.Server testWsConn *websocket.Conn - shipDataProcessing *MockShipDataProcessing + shipDataProcessing *MockWebsocketDataProcessing } func (s *WebsocketSuite) SetupSuite() {} @@ -37,7 +37,7 @@ func (s *WebsocketSuite) TearDownTest() {} func (s *WebsocketSuite) BeforeTest(suiteName, testName string) { ctrl := gomock.NewController(s.T()) - s.shipDataProcessing = NewMockShipDataProcessing(ctrl) + s.shipDataProcessing = NewMockWebsocketDataProcessing(ctrl) s.shipDataProcessing.EXPECT().ReportConnectionError(gomock.Any()).AnyTimes() s.shipDataProcessing.EXPECT().HandleIncomingShipMessage(gomock.Any()).AnyTimes() diff --git a/spine/api.go b/spine/api.go index a9a01649..f403cb26 100644 --- a/spine/api.go +++ b/spine/api.go @@ -3,6 +3,7 @@ package spine import ( "time" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine/model" ) @@ -23,7 +24,7 @@ type DeviceLocal interface { Device RemoveRemoteDeviceConnection(ski string) AddRemoteDeviceForSki(ski string, rDevice DeviceRemote) - AddRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing + SetupRemoteDevice(ski string, writeI ship.SpineDataConnection) ship.SpineDataProcessing RemoveRemoteDevice(ski string) RemoteDevices() []DeviceRemote RemoteDeviceForAddress(address model.AddressDeviceType) DeviceRemote @@ -47,7 +48,7 @@ type DeviceRemote interface { Device Ski() string SetAddress(address *model.AddressDeviceType) - HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) + HandleSpineMesssage(message []byte) (*model.MsgCounterType, error) Sender() Sender Entity(id []model.AddressEntityType) EntityRemote Entities() []EntityRemote @@ -67,12 +68,6 @@ type DeviceRemote interface { CheckEntityInformation(initialData bool, entity model.NodeManagementDetailedDiscoveryEntityInformationType) error } -// implemented by spine.DeviceLocalImpl and used by shipConnection -type DeviceLocalConnection interface { - RemoveRemoteDeviceConnection(ski string) - AddRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing -} - /* Entity */ type Entity interface { @@ -256,7 +251,7 @@ type PendingRequests interface { /* Bindings */ -// implemented by spine.BindingManagerImpl +// implemented by BindingManagerImpl type BindingManager interface { AddBinding(remoteDevice DeviceRemote, data model.BindingManagementRequestCallType) error RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice DeviceRemote) error @@ -286,23 +281,3 @@ type HeartbeatManager interface { StartHeartbeat() error StopHeartbeat() } - -/* Processing */ - -//go:generate mockery --name=SpineDataProcessing - -// Used to pass an incoming SPINE message from a SHIP connection to the proper DeviceRemoteImpl -// -// Implemented by DeviceRemoteImpl, used by ShipConnection -type SpineDataProcessing interface { - HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) -} - -//go:generate mockery --name=SpineDataConnection - -// Used to pass an outgoing SPINE message from a DeviceLocalImpl to the SHIP connection -// -// Implemented by ShipConnection, used by DeviceLocalImpl -type SpineDataConnection interface { - WriteSpineMessage(message []byte) -} diff --git a/spine/binding_manager_test.go b/spine/binding_manager_test.go index f9b9fbbe..77557e93 100644 --- a/spine/binding_manager_test.go +++ b/spine/binding_manager_test.go @@ -18,22 +18,23 @@ type BindingManagerSuite struct { suite.Suite localDevice DeviceLocal + writeHandler *WriteMessageHandler remoteDevice DeviceRemote - sut BindingManager -} -func (suite *BindingManagerSuite) WriteSpineMessage([]byte) {} + sut BindingManager +} -func (suite *BindingManagerSuite) SetupSuite() { - suite.localDevice = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) +func (s *BindingManagerSuite) BeforeTest(suiteName, testName string) { + s.localDevice = NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", + "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) + remoteSki := "TestRemoteSki" - ski := "test" - sender := NewSender(suite) - suite.remoteDevice = NewDeviceRemoteImpl(suite.localDevice, ski, sender) + s.writeHandler = &WriteMessageHandler{} - suite.localDevice.AddRemoteDevice(ski, suite) + sender := NewSender(s.writeHandler) + s.remoteDevice = NewDeviceRemoteImpl(s.localDevice, remoteSki, sender) - suite.sut = NewBindingManager(suite.localDevice) + s.sut = NewBindingManager(s.localDevice) } func (suite *BindingManagerSuite) Test_Bindings() { diff --git a/spine/device_local.go b/spine/device_local.go index 832d5048..c8333059 100644 --- a/spine/device_local.go +++ b/spine/device_local.go @@ -8,6 +8,7 @@ import ( "time" "github.com/enbility/eebus-go/logging" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" ) @@ -37,7 +38,11 @@ type DeviceLocalImpl struct { // SerialNumber is the serial number // DeviceCode is the SHIP id (accessMethods.id) // DeviceAddress is the SPINE device address -func NewDeviceLocalImpl(brandName, deviceModel, serialNumber, deviceCode, deviceAddress string, deviceType model.DeviceTypeType, featureSet model.NetworkManagementFeatureSetType, heartbeatTimeout time.Duration) *DeviceLocalImpl { +func NewDeviceLocalImpl( + brandName, deviceModel, serialNumber, deviceCode, deviceAddress string, + deviceType model.DeviceTypeType, + featureSet model.NetworkManagementFeatureSetType, + heartbeatTimeout time.Duration) *DeviceLocalImpl { address := model.AddressDeviceType(deviceAddress) var fSet *model.NetworkManagementFeatureSetType @@ -62,8 +67,6 @@ func NewDeviceLocalImpl(brandName, deviceModel, serialNumber, deviceCode, device return res } -var _ DeviceLocalConnection = (*DeviceLocalImpl)(nil) - func (r *DeviceLocalImpl) RemoveRemoteDeviceConnection(ski string) { remoteDevice := r.RemoteDeviceForSki(ski) @@ -86,8 +89,8 @@ func (r *DeviceLocalImpl) AddRemoteDeviceForSki(ski string, rDevice DeviceRemote r.mux.Unlock() } -// Adds a new remote device with a given SKI and triggers SPINE requesting device details -func (r *DeviceLocalImpl) AddRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing { +// Setup a new remote device with a given SKI and triggers SPINE requesting device details +func (r *DeviceLocalImpl) SetupRemoteDevice(ski string, writeI ship.SpineDataConnection) ship.SpineDataProcessing { sender := NewSender(writeI) rDevice := NewDeviceRemoteImpl(r, ski, sender) diff --git a/spine/device_local_test.go b/spine/device_local_test.go index a7b1960a..9de7ff30 100644 --- a/spine/device_local_test.go +++ b/spine/device_local_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" @@ -18,7 +19,7 @@ type DeviceLocalTestSuite struct { suite.Suite } -var _ SpineDataConnection = (*DeviceLocalTestSuite)(nil) +var _ ship.SpineDataConnection = (*DeviceLocalTestSuite)(nil) func (d *DeviceLocalTestSuite) WriteSpineMessage([]byte) {} @@ -26,7 +27,7 @@ func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() { sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) ski := "test" - sut.AddRemoteDevice(ski, d) + _ = sut.SetupRemoteDevice(ski, d) rDevice := sut.RemoteDeviceForSki(ski) assert.NotNil(d.T(), rDevice) @@ -53,7 +54,7 @@ func (d *DeviceLocalTestSuite) Test_RemoteDevice() { devices := sut.RemoteDevices() assert.Equal(d.T(), 0, len(devices)) - sut.AddRemoteDevice(ski, d) + _ = sut.SetupRemoteDevice(ski, d) remote = sut.RemoteDeviceForSki(ski) assert.NotNil(d.T(), remote) @@ -94,7 +95,7 @@ func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() { sut.AddEntity(localEntity) ski := "test" - sut.AddRemoteDevice(ski, d) + _ = sut.SetupRemoteDevice(ski, d) remote := sut.RemoteDeviceForSki(ski) assert.NotNil(d.T(), remote) @@ -153,7 +154,7 @@ func (d *DeviceLocalTestSuite) Test_ProcessCmd() { ski := "test" remoteDeviceName := "remote" - sut.AddRemoteDevice(ski, d) + _ = sut.SetupRemoteDevice(ski, d) remote := sut.RemoteDeviceForSki(ski) assert.NotNil(d.T(), remote) diff --git a/spine/device_remote.go b/spine/device_remote.go index 29bf8cde..f32fe3a2 100644 --- a/spine/device_remote.go +++ b/spine/device_remote.go @@ -8,6 +8,7 @@ import ( "sync" "github.com/enbility/eebus-go/logging" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine/model" ) @@ -26,7 +27,7 @@ type DeviceRemoteImpl struct { localDevice DeviceLocal } -var _ SpineDataProcessing = (*DeviceRemoteImpl)(nil) +var _ ship.SpineDataProcessing = (*DeviceRemoteImpl)(nil) func NewDeviceRemoteImpl(localDevice DeviceLocal, ski string, sender Sender) *DeviceRemoteImpl { res := DeviceRemoteImpl{ @@ -49,12 +50,7 @@ func (d *DeviceRemoteImpl) SetAddress(address *model.AddressDeviceType) { d.address = address } -// // this connection is closed -// func (d *DeviceRemoteImpl) CloseConnection() { -// } - -// processing incoming SPINE message from the associated SHIP connection -func (d *DeviceRemoteImpl) HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) { +func (d *DeviceRemoteImpl) HandleSpineMesssage(message []byte) (*model.MsgCounterType, error) { datagram := model.Datagram{} if err := json.Unmarshal([]byte(message), &datagram); err != nil { return nil, err @@ -67,6 +63,11 @@ func (d *DeviceRemoteImpl) HandleIncomingSpineMesssage(message []byte) (*model.M return datagram.Datagram.Header.MsgCounter, nil } +// processing incoming SPINE message from the associated SHIP connection +func (d *DeviceRemoteImpl) HandleIncomingSpineMesssage(message []byte) { + _, _ = d.HandleSpineMesssage(message) +} + func (d *DeviceRemoteImpl) addNodeManagement() { deviceInformation := d.addNewEntity(model.EntityTypeTypeDeviceInformation, NewAddressEntityType([]uint{DeviceInformationEntityId})) nodeManagement := NewFeatureRemoteImpl(deviceInformation.NextFeatureId(), deviceInformation, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go index 4f11f1b8..cd860979 100644 --- a/spine/device_remote_test.go +++ b/spine/device_remote_test.go @@ -37,7 +37,7 @@ func (s *DeviceRemoteSuite) BeforeTest(suiteName, testName string) { sender := NewSender(s) s.remoteDevice = NewDeviceRemoteImpl(s.localDevice, ski, sender) s.remoteDevice.SetAddress(util.Ptr(model.AddressDeviceType("test"))) - s.localDevice.AddRemoteDevice(ski, s) + _ = s.localDevice.SetupRemoteDevice(ski, s) s.remoteEntity = NewEntityRemoteImpl(s.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) @@ -84,14 +84,14 @@ func (s *DeviceRemoteSuite) Test_Usecases() { uc := s.remoteDevice.UseCases() assert.Nil(s.T(), uc) - _, _ = s.remoteDevice.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) + _, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) uc = s.remoteDevice.UseCases() assert.NotNil(s.T(), uc) } func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport_ElliJSON() { - _, _ = s.remoteDevice.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) + _, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) result := s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( model.UseCaseActorTypeBatterySystem, diff --git a/spine/heartbeat_manager_test.go b/spine/heartbeat_manager_test.go index 4b5c2cb8..3619fca5 100644 --- a/spine/heartbeat_manager_test.go +++ b/spine/heartbeat_manager_test.go @@ -31,7 +31,7 @@ func (suite *HeartBeatManagerSuite) SetupSuite() { sender := NewSender(suite) suite.remoteDevice = NewDeviceRemoteImpl(suite.localDevice, ski, sender) - suite.localDevice.AddRemoteDevice(ski, suite) + _ = suite.localDevice.SetupRemoteDevice(ski, suite) suite.sut = suite.localDevice.HeartbeatManager() } diff --git a/spine/helper_test.go b/spine/helper_test.go index 208d676e..06ad3c72 100644 --- a/spine/helper_test.go +++ b/spine/helper_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" "github.com/google/go-cmp/cmp" @@ -26,7 +27,7 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ SpineDataConnection = (*WriteMessageHandler)(nil) +var _ ship.SpineDataConnection = (*WriteMessageHandler)(nil) func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { t.mux.Lock() diff --git a/spine/mocks/SpineDataProcessing.go b/spine/mocks/SpineDataProcessing.go deleted file mode 100644 index 11a48825..00000000 --- a/spine/mocks/SpineDataProcessing.go +++ /dev/null @@ -1,57 +0,0 @@ -// Code generated by mockery v2.39.1. DO NOT EDIT. - -package mocks - -import ( - model "github.com/enbility/eebus-go/spine/model" - mock "github.com/stretchr/testify/mock" -) - -// SpineDataProcessing is an autogenerated mock type for the SpineDataProcessing type -type SpineDataProcessing struct { - mock.Mock -} - -// HandleIncomingSpineMesssage provides a mock function with given fields: message -func (_m *SpineDataProcessing) HandleIncomingSpineMesssage(message []byte) (*model.MsgCounterType, error) { - ret := _m.Called(message) - - if len(ret) == 0 { - panic("no return value specified for HandleIncomingSpineMesssage") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func([]byte) (*model.MsgCounterType, error)); ok { - return rf(message) - } - if rf, ok := ret.Get(0).(func([]byte) *model.MsgCounterType); ok { - r0 = rf(message) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func([]byte) error); ok { - r1 = rf(message) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewSpineDataProcessing creates a new instance of SpineDataProcessing. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewSpineDataProcessing(t interface { - mock.TestingT - Cleanup(func()) -}) *SpineDataProcessing { - mock := &SpineDataProcessing{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/spine/nodemanagement_detaileddiscovery.go b/spine/nodemanagement_detaileddiscovery.go index 855d55c2..992f37f8 100644 --- a/spine/nodemanagement_detaileddiscovery.go +++ b/spine/nodemanagement_detaileddiscovery.go @@ -209,7 +209,7 @@ func (r *NodeManagementImpl) processNotifyDetailedDiscoveryData(message *Message return nil } -// func (f *NodeManagement) announceFeatureDiscovery(e spine.Entity) error { +// func (f *NodeManagement) announceFeatureDiscovery(e Entity) error { // entity := f.Entity() // if entity == nil { // return errors.New("announceFeatureDiscovery: entity not found") @@ -232,7 +232,7 @@ func (r *NodeManagementImpl) processNotifyDetailedDiscoveryData(message *Message // rr := rf.Role() // rolesValid := (lr == model.RoleTypeSpecial && rr == model.RoleTypeSpecial) || (lr == model.RoleTypeClient && rr == model.RoleTypeServer) // if lf.Type() == rf.Type() && rolesValid { -// if cf, ok := lf.(spine.ClientFeature); ok { +// if cf, ok := lf.(ClientFeature); ok { // if err := cf.ServerFound(rf); err != nil { // return err // } diff --git a/spine/nodemanagement_detaileddiscovery_test.go b/spine/nodemanagement_detaileddiscovery_test.go index 938c532d..dfa5a984 100644 --- a/spine/nodemanagement_detaileddiscovery_test.go +++ b/spine/nodemanagement_detaileddiscovery_test.go @@ -31,11 +31,8 @@ type NodeManagementSuite struct { remoteSki string - readHandler SpineDataProcessing writeHandler *WriteMessageHandler -} - -func (s *NodeManagementSuite) SetupSuite() { + remoteDevice DeviceRemote } func (s *NodeManagementSuite) BeforeTest(suiteName, testName string) { @@ -44,11 +41,8 @@ func (s *NodeManagementSuite) BeforeTest(suiteName, testName string) { s.remoteSki = "TestRemoteSki" s.writeHandler = &WriteMessageHandler{} - - s.readHandler = s.sut.AddRemoteDevice(s.remoteSki, s.writeHandler) -} - -func (s *NodeManagementSuite) AfterTest(suiteName, testName string) { + _ = s.sut.SetupRemoteDevice(s.remoteSki, s.writeHandler) + s.remoteDevice = s.sut.RemoteDeviceForSki(s.remoteSki) } func (s *NodeManagementSuite) TestDetailedDiscovery_SendRead() { @@ -61,7 +55,7 @@ func (s *NodeManagementSuite) TestDetailedDiscovery_SendRead() { func (s *NodeManagementSuite) TestDetailedDiscovery_SendReply() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_detaileddiscoverydata_recv_read_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_detaileddiscoverydata_recv_read_file_path)) // Assert sendBytes := s.writeHandler.MessageWithReference(msgCounter) @@ -70,7 +64,7 @@ func (s *NodeManagementSuite) TestDetailedDiscovery_SendReply() { func (s *NodeManagementSuite) TestDetailedDiscovery_RecvReply() { // Act - _, _ = s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_reply_file_path)) + _, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_reply_file_path)) // Assert remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) @@ -127,10 +121,10 @@ func (s *NodeManagementSuite) TestDetailedDiscovery_RecvReply() { } func (s *NodeManagementSuite) TestDetailedDiscovery_RecvNotifyAdded() { - _, _ = s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_reply_file_path)) + _, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_reply_file_path)) // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_notify_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_notify_file_path)) waitForAck(s.T(), msgCounter, s.writeHandler) // Assert @@ -164,7 +158,7 @@ func (s *NodeManagementSuite) TestDetailedDiscovery_RecvNotifyAdded() { func (s *NodeManagementSuite) TestDetailedDiscovery_SendReplyWithAcknowledge() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_detaileddiscoverydata_recv_read_ack_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_detaileddiscoverydata_recv_read_ack_file_path)) // Assert sentReply := s.writeHandler.MessageWithReference(msgCounter) @@ -175,7 +169,7 @@ func (s *NodeManagementSuite) TestDetailedDiscovery_SendReplyWithAcknowledge() { func (s *NodeManagementSuite) TestSubscriptionRequestCall_BeforeDetailedDiscovery() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_subscriptionRequestCall_recv_call_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_subscriptionRequestCall_recv_call_file_path)) // Assert sentResult := s.writeHandler.ResultWithReference(msgCounter) @@ -190,7 +184,7 @@ func (s *NodeManagementSuite) TestSubscriptionRequestCall_BeforeDetailedDiscover func (s *NodeManagementSuite) TestDestinationList_SendReply() { // Act - msgCounter, _ := s.readHandler.HandleIncomingSpineMesssage(loadFileData(s.T(), nm_destinationListData_recv_read_file_path)) + msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_destinationListData_recv_read_file_path)) // Assert sendBytes := s.writeHandler.MessageWithReference(msgCounter) diff --git a/spine/operations_test.go b/spine/operations_test.go index 77c4e844..b9cbdbee 100644 --- a/spine/operations_test.go +++ b/spine/operations_test.go @@ -1,14 +1,13 @@ -package spine_test +package spine import ( "testing" - "github.com/enbility/eebus-go/spine" "github.com/stretchr/testify/assert" ) func TestOperations(t *testing.T) { - operations := spine.NewOperations(true, false) + operations := NewOperations(true, false) assert.NotNil(t, operations) text := operations.String() @@ -17,7 +16,7 @@ func TestOperations(t *testing.T) { data := operations.Information() assert.NotNil(t, data) - operations2 := spine.NewOperations(true, true) + operations2 := NewOperations(true, true) assert.NotNil(t, operations2) text = operations2.String() @@ -26,7 +25,7 @@ func TestOperations(t *testing.T) { data = operations2.Information() assert.NotNil(t, data) - operations3 := spine.NewOperations(false, false) + operations3 := NewOperations(false, false) assert.NotNil(t, operations3) text = operations3.String() diff --git a/spine/send.go b/spine/send.go index 0b28d1a7..7b89a8fe 100644 --- a/spine/send.go +++ b/spine/send.go @@ -6,6 +6,7 @@ import ( "sync/atomic" "github.com/enbility/eebus-go/logging" + "github.com/enbility/eebus-go/ship" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" lru "github.com/hashicorp/golang-lru/v2" @@ -17,12 +18,12 @@ type SenderImpl struct { // we cache the last 100 notify messages, so we can find the matching item for result errors being returned datagramNotifyCache *lru.Cache[model.MsgCounterType, model.DatagramType] - writeHandler SpineDataConnection + writeHandler ship.SpineDataConnection } var _ Sender = (*SenderImpl)(nil) -func NewSender(writeI SpineDataConnection) Sender { +func NewSender(writeI ship.SpineDataConnection) Sender { cache, _ := lru.New[model.MsgCounterType, model.DatagramType](100) return &SenderImpl{ datagramNotifyCache: cache, diff --git a/spine/subscription_manager_test.go b/spine/subscription_manager_test.go index c14ae58b..22a57412 100644 --- a/spine/subscription_manager_test.go +++ b/spine/subscription_manager_test.go @@ -31,7 +31,7 @@ func (suite *SubscriptionManagerSuite) SetupSuite() { sender := NewSender(suite) suite.remoteDevice = NewDeviceRemoteImpl(suite.localDevice, ski, sender) - suite.localDevice.AddRemoteDevice(ski, suite) + _ = suite.localDevice.SetupRemoteDevice(ski, suite) suite.sut = NewSubscriptionManager(suite.localDevice) } From 8266a6b2447a0dced5b75e955d7218c793e74129 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 13 Jan 2024 21:17:59 +0100 Subject: [PATCH 153/240] Fix linter warnings --- features/helper_test.go | 42 ----------------------------------------- 1 file changed, 42 deletions(-) diff --git a/features/helper_test.go b/features/helper_test.go index 1d9b8fab..bcfa2367 100644 --- a/features/helper_test.go +++ b/features/helper_test.go @@ -2,10 +2,7 @@ package features_test import ( "encoding/json" - "fmt" - "os" "sync" - "testing" "time" "github.com/enbility/eebus-go/ship" @@ -15,16 +12,6 @@ import ( "github.com/stretchr/testify/assert" ) -const ( - ec_permittedvaluesetlistdata_recv_notify_partial_file_path = "../spine/testdata/ec_permittedvaluesetlistdata_recv_notify_partial.json" - ec_descriptionlistdata_recv_reply_file_path = "../spine/testdata/ec_descriptionListData_recv_reply.json" - ec_parameterdescriptionlistdata_recv_reply_file_path = "../spine/testdata/ec_parameterDescriptionListData_recv_reply.json" - ec_subscriptionRequestCall_recv_result_file_path = "../spine/testdata/ec_subscriptionRequestCall_recv_result.json" - m_subscriptionRequestCall_recv_result_file_path = "../spine/testdata/m_subscriptionRequestCall_recv_result.json" - m_descriptionListData_recv_reply_file_path = "../spine/testdata/m_descriptionListData_recv_reply.json" - m_measurementListData_recv_notify_file_path = "../spine/testdata/m_measurementListData_recv_notify.json" -) - type featureFunctions struct { featureType model.FeatureTypeType functions []model.FunctionType @@ -108,35 +95,6 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg return nil } -func loadFileData(t *testing.T, fileName string) []byte { - fileData, err := os.ReadFile(fileName) - if err != nil { - t.Fatal(err) - } - - return fileData -} - -func waitForAck(t *testing.T, msgCounterReference *model.MsgCounterType, writeHandler *WriteMessageHandler) { - var datagram model.Datagram - - msg := writeHandler.ResultWithReference(msgCounterReference) - if msg == nil { - t.Fatal("acknowledge message was not sent!!") - } - - if err := json.Unmarshal(msg, &datagram); err != nil { - t.Fatal(err) - } - - cmd := datagram.Datagram.Payload.Cmd[0] - if cmd.ResultData != nil { - if cmd.ResultData.ErrorNumber != nil && uint(*cmd.ResultData.ErrorNumber) != uint(model.ErrorNumberTypeNoError) { - t.Fatal(fmt.Errorf("error '%d' result data received", uint(*cmd.ResultData.ErrorNumber))) - } - } -} - func setupFeatures(t assert.TestingT, dataCon ship.SpineDataConnection, featureFunctions []featureFunctions) (spine.EntityLocal, spine.EntityRemote) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) From 68282c5f9d9e127be1a4593f90db1631337ce6dc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 14 Jan 2024 20:55:36 +0100 Subject: [PATCH 154/240] Switch to external SHIP and SPINE packages The SHIP and SPINE implementations have been moved to independant repositories and go packges: - SHIP: https://github.com/enbility/ship-go - SPINE: https://github.com/enbility/spine-go --- LICENSE | 1 + README.md | 154 +-- api/.mockery.yaml | 9 + {service => api}/api.go | 46 +- service/types.go => api/configuration.go | 185 +-- .../configuration_test.go | 72 +- api/connectionstate.go | 95 ++ api/connectionstate_test.go | 29 + api/servicedetails.go | 67 + api/servicedetails_test.go | 29 + {service => cert}/cert.go | 6 +- {service => cert}/cert_test.go | 10 +- cmd/evse/main.go | 20 +- cmd/hems/main.go | 20 +- features/api.go | 6 +- features/deviceclassification.go | 6 +- features/deviceclassification_test.go | 12 +- features/deviceconfiguration.go | 6 +- features/deviceconfiguration_test.go | 12 +- features/devicediagnosis.go | 6 +- features/devicediagnosis_test.go | 12 +- features/electricalconnection.go | 6 +- features/electricalconnection_test.go | 12 +- features/feature.go | 24 +- features/helper_test.go | 11 +- features/identification.go | 6 +- features/identification_test.go | 12 +- features/incentivetable.go | 6 +- features/incentivetable_test.go | 12 +- features/loadcontrol.go | 6 +- features/loadcontrol_test.go | 12 +- features/measurement.go | 6 +- features/measurement_test.go | 12 +- features/timeseries.go | 6 +- features/timeseries_test.go | 12 +- go.mod | 35 +- go.sum | 19 +- integration_tests/devicediagnosis_test.go | 53 - .../electricalconnection_test.go | 148 -- .../emobility_measurement_test.go | 24 +- integration_tests/helper_test.go | 34 +- integration_tests/measurement_test.go | 163 --- .../dd_subscriptionRequestCall_recv.json | 41 - .../ec_descriptionListData_recv_reply.json | 37 - ...dvaluesetlistdata_recv_notify_partial.json | 98 -- ...c_subscriptionRequestCall_recv_result.json | 33 - ...box_detaileddiscoverydata_recv_notify.json | 0 ...lbox_detaileddiscoverydata_recv_reply.json | 0 logging/log.go | 52 - logging/mocks/Logging.go | 84 -- mdns/.mockery.yaml | 9 + {service/mdns => mdns}/api.go | 2 +- {service/mdns => mdns}/avahi.go | 2 +- {service/mdns => mdns}/helper.go | 0 {service/mdns => mdns}/types.go | 0 {service/mdns => mdns}/zeroconf.go | 2 +- mocks/ConnectionsHub.go | 393 ++++++ mocks/EEBUSService.go | 522 ++++++++ mocks/EEBUSServiceHandler.go | 251 ++++ mocks/MdnsProvider.go | 227 ++++ mocks/MdnsSearch.go | 68 + mocks/MdnsService.go | 255 ++++ mocks/ServiceProvider.go | 248 ++++ mocks/mockgen_api.go | 213 +++ service/hub.go | 188 +-- service/hub_test.go | 298 +++-- service/mdns.go | 69 +- service/mdns/mocks/MdnsProvider.go | 79 -- service/mdns_test.go | 37 +- service/mock_hub_test.go | 260 ---- service/mock_mdns_test.go | 150 --- service/mock_service_test.go | 113 -- service/service.go | 118 +- service/service_test.go | 71 +- ship/api.go | 80 -- ship/connection.go | 411 ------ ship/connection_test.go | 222 --- ship/handshake.go | 282 ---- ship/hs_access.go | 81 -- ship/hs_access_test.go | 160 --- ship/hs_hello.go | 258 ---- ship/hs_hello_client_test.go | 60 - ship/hs_hello_test.go | 350 ----- ship/hs_helper_test.go | 64 - ship/hs_init.go | 72 - ship/hs_init_client_test.go | 99 -- ship/hs_init_server_test.go | 83 -- ship/hs_pin.go | 50 - ship/hs_pin_test.go | 122 -- ship/hs_prot.go | 175 --- ship/hs_prot_client_test.go | 127 -- ship/hs_prot_server_test.go | 146 -- ship/mock_types_test.go | 310 ----- ship/mocks/ShipConnection.go | 108 -- ship/mocks/WebsocketDataConnection.go | 83 -- ship/model/model.go | 189 --- ship/types.go | 111 -- ship/util/helper.go | 78 -- ship/util/helper_test.go | 76 -- ship/websocket.go | 303 ----- ship/websocket_test.go | 184 --- spine/api.go | 283 ---- spine/binding_manager.go | 226 ---- spine/binding_manager_test.go | 108 -- spine/const.go | 5 - spine/device.go | 54 - spine/device_local.go | 431 ------ spine/device_local_test.go | 237 ---- spine/device_remote.go | 360 ----- spine/device_remote_test.go | 275 ---- spine/entity.go | 86 -- spine/entity_local.go | 164 --- spine/entity_local_test.go | 80 -- spine/entity_remote.go | 45 - spine/events.go | 141 -- spine/feature.go | 73 - spine/feature_local.go | 465 ------- spine/feature_local_test.go | 190 --- spine/feature_remote.go | 114 -- spine/function_data.go | 82 -- spine/function_data_cmd.go | 120 -- spine/function_data_cmd_test.go | 1140 ---------------- spine/function_data_factory.go | 318 ----- spine/function_data_factory_test.go | 96 -- spine/function_data_test.go | 79 -- spine/heartbeat_manager.go | 147 -- spine/heartbeat_manager_test.go | 138 -- spine/helper_test.go | 196 --- spine/message.go | 23 - spine/mocks/Sender.go | 319 ----- spine/mocks/SpineDataConnection.go | 29 - spine/model/actuatorlevel.go | 36 - spine/model/actuatorswitch.go | 27 - spine/model/alarm.go | 44 - spine/model/alarm_additions.go | 14 - spine/model/alarm_additions_test.go | 46 - spine/model/bill.go | 157 --- spine/model/bill_additions.go | 40 - spine/model/bill_additions_test.go | 122 -- spine/model/bindingmanagement.go | 53 - spine/model/bindingmanagement_additions.go | 14 - .../model/bindingmanagement_additions_test.go | 46 - spine/model/collection_operations.go | 122 -- spine/model/collection_operations_test.go | 62 - spine/model/commandframe.go | 426 ------ spine/model/commandframe_additions.go | 297 ----- spine/model/commandframe_additions_test.go | 141 -- spine/model/commondatatypes.go | 988 -------------- spine/model/commondatatypes_additions.go | 234 ---- spine/model/commondatatypes_additions_test.go | 282 ---- spine/model/custom.go | 47 - spine/model/custom_test.go | 62 - spine/model/datagram.go | 26 - spine/model/datagram_additions.go | 42 - spine/model/datagram_additions_test.go | 116 -- spine/model/datatunneling.go | 27 - spine/model/deviceclassification.go | 55 - spine/model/deviceconfiguration.go | 150 --- spine/model/deviceconfiguration_additions.go | 40 - .../deviceconfiguration_additions_test.go | 134 -- spine/model/devicediagnosis.go | 76 -- spine/model/directcontrol.go | 55 - spine/model/eebus_tags.go | 46 - spine/model/electricalconnection.go | 241 ---- spine/model/electricalconnection_additions.go | 66 - .../electricalconnection_additions_test.go | 1188 ----------------- spine/model/hvac.go | 222 --- spine/model/hvac_additions.go | 105 -- spine/model/hvac_additions_test.go | 312 ----- spine/model/identification.go | 82 -- spine/model/identification_additions.go | 40 - spine/model/identification_additions_test.go | 46 - spine/model/incentivetable.go | 103 -- spine/model/loadcontrol.go | 185 --- spine/model/loadcontrol_additions.go | 66 - spine/model/loadcontrol_additions_test.go | 198 --- spine/model/measurement.go | 222 --- spine/model/measurement_additions.go | 66 - spine/model/measurement_additions_test.go | 207 --- spine/model/messaging.go | 39 - spine/model/messaging_additions.go | 14 - spine/model/messaging_additions_test.go | 46 - spine/model/networkmanagement.go | 239 ---- spine/model/networkmanagement_additions.go | 40 - spine/model/nodemanagement.go | 136 -- spine/model/nodemanagement_additions.go | 143 -- spine/model/nodemanagement_additions_test.go | 123 -- spine/model/operatingconstraints.go | 143 -- spine/model/operatingconstraints_additions.go | 79 -- .../operatingconstraints_additions_test.go | 239 ---- spine/model/powersequences.go | 331 ----- spine/model/powersequences_additions.go | 131 -- spine/model/powersequences_additions_test.go | 391 ------ spine/model/result.go | 21 - spine/model/sensing.go | 99 -- spine/model/sensing_additions.go | 14 - spine/model/setpoint.go | 98 -- spine/model/setpoint_additions.go | 27 - spine/model/setpoint_additions_test.go | 84 -- spine/model/smartenergymanagementps.go | 104 -- spine/model/stateinformation.go | 115 -- spine/model/stateinformation_additions.go | 14 - spine/model/subscriptionmanagement.go | 53 - .../model/subscriptionmanagement_additions.go | 14 - .../subscriptionmanagement_additions_test.go | 46 - spine/model/supplyconditions.go | 112 -- spine/model/supplyconditions_additions.go | 40 - .../model/supplyconditions_additions_test.go | 122 -- spine/model/tariffinformation.go | 370 ----- spine/model/tariffinformation_additions.go | 157 --- .../model/tariffinformation_additions_test.go | 464 ------- spine/model/taskmanagement.go | 159 --- spine/model/taskmanagement_additions.go | 40 - spine/model/taskmanagement_additions_test.go | 128 -- spine/model/threshold.go | 85 -- spine/model/threshold_additions.go | 40 - spine/model/threshold_additions_test.go | 122 -- spine/model/timeinformation.go | 41 - spine/model/timeseries.go | 133 -- spine/model/timeseries_additions.go | 40 - spine/model/timeseries_additions_test.go | 239 ---- spine/model/timetable.go | 96 -- spine/model/timetable_additions.go | 40 - spine/model/timetable_additions_test.go | 128 -- spine/model/update.go | 301 ----- spine/model/update_test.go | 120 -- spine/model/usecaseinformation.go | 118 -- spine/model/usecaseinformation_additions.go | 53 - .../usecaseinformation_additions_test.go | 47 - spine/model/version.go | 11 - spine/model/version_additions.go | 14 - spine/model/version_additions_test.go | 48 - spine/nodemanagement.go | 111 -- spine/nodemanagement_binding.go | 80 -- spine/nodemanagement_destinationlist.go | 50 - spine/nodemanagement_detaileddiscovery.go | 265 ---- .../nodemanagement_detaileddiscovery_test.go | 192 --- spine/nodemanagement_subscription.go | 93 -- spine/nodemanagement_test.go | 155 --- spine/nodemanagement_usecase.go | 73 - spine/operations.go | 39 - spine/operations_test.go | 36 - spine/pending_requests.go | 112 -- spine/pending_requests_test.go | 123 -- spine/send.go | 273 ---- spine/send_test.go | 177 --- spine/subscription_manager.go | 227 ---- spine/subscription_manager_test.go | 96 -- .../nm_destinationListData_recv_read.json | 29 - ...stinationListData_send_reply_expected.json | 43 - .../nm_detaileddiscoverydata_recv_read.json | 29 - ...m_detaileddiscoverydata_recv_read_ack.json | 30 - ...aileddiscoverydata_send_read_expected.json | 29 - ...ileddiscoverydata_send_reply_expected.json | 143 -- ...leddiscoverydata_send_result_expected.json | 33 - .../nm_subscriptionRequestCall_recv_call.json | 48 - ...ptionRequestCall_send_result_expected.json | 33 - ...usecaseinformationlistdata_recv_reply.json | 120 -- util/channel.go | 13 - util/type.go | 13 - util/zero.go | 15 - 261 files changed, 3147 insertions(+), 28610 deletions(-) create mode 100644 api/.mockery.yaml rename {service => api}/api.go (67%) rename service/types.go => api/configuration.go (60%) rename service/types_test.go => api/configuration_test.go (51%) create mode 100644 api/connectionstate.go create mode 100644 api/connectionstate_test.go create mode 100644 api/servicedetails.go create mode 100644 api/servicedetails_test.go rename {service => cert}/cert.go (96%) rename {service => cert}/cert_test.go (92%) delete mode 100644 integration_tests/devicediagnosis_test.go delete mode 100644 integration_tests/electricalconnection_test.go delete mode 100644 integration_tests/measurement_test.go delete mode 100644 integration_tests/testdata/dd_subscriptionRequestCall_recv.json delete mode 100644 integration_tests/testdata/ec_descriptionListData_recv_reply.json delete mode 100644 integration_tests/testdata/ec_permittedvaluesetlistdata_recv_notify_partial.json delete mode 100644 integration_tests/testdata/ec_subscriptionRequestCall_recv_result.json rename {spine => integration_tests}/testdata/wallbox_detaileddiscoverydata_recv_notify.json (100%) rename {spine => integration_tests}/testdata/wallbox_detaileddiscoverydata_recv_reply.json (100%) delete mode 100644 logging/log.go delete mode 100644 logging/mocks/Logging.go create mode 100644 mdns/.mockery.yaml rename {service/mdns => mdns}/api.go (88%) rename {service/mdns => mdns}/avahi.go (99%) rename {service/mdns => mdns}/helper.go (100%) rename {service/mdns => mdns}/types.go (100%) rename {service/mdns => mdns}/zeroconf.go (98%) create mode 100644 mocks/ConnectionsHub.go create mode 100644 mocks/EEBUSService.go create mode 100644 mocks/EEBUSServiceHandler.go create mode 100644 mocks/MdnsProvider.go create mode 100644 mocks/MdnsSearch.go create mode 100644 mocks/MdnsService.go create mode 100644 mocks/ServiceProvider.go create mode 100644 mocks/mockgen_api.go delete mode 100644 service/mdns/mocks/MdnsProvider.go delete mode 100644 service/mock_hub_test.go delete mode 100644 service/mock_mdns_test.go delete mode 100644 service/mock_service_test.go delete mode 100644 ship/api.go delete mode 100644 ship/connection.go delete mode 100644 ship/connection_test.go delete mode 100644 ship/handshake.go delete mode 100644 ship/hs_access.go delete mode 100644 ship/hs_access_test.go delete mode 100644 ship/hs_hello.go delete mode 100644 ship/hs_hello_client_test.go delete mode 100644 ship/hs_hello_test.go delete mode 100644 ship/hs_helper_test.go delete mode 100644 ship/hs_init.go delete mode 100644 ship/hs_init_client_test.go delete mode 100644 ship/hs_init_server_test.go delete mode 100644 ship/hs_pin.go delete mode 100644 ship/hs_pin_test.go delete mode 100644 ship/hs_prot.go delete mode 100644 ship/hs_prot_client_test.go delete mode 100644 ship/hs_prot_server_test.go delete mode 100644 ship/mock_types_test.go delete mode 100644 ship/mocks/ShipConnection.go delete mode 100644 ship/mocks/WebsocketDataConnection.go delete mode 100644 ship/model/model.go delete mode 100644 ship/types.go delete mode 100644 ship/util/helper.go delete mode 100644 ship/util/helper_test.go delete mode 100644 ship/websocket.go delete mode 100644 ship/websocket_test.go delete mode 100644 spine/api.go delete mode 100644 spine/binding_manager.go delete mode 100644 spine/binding_manager_test.go delete mode 100644 spine/const.go delete mode 100644 spine/device.go delete mode 100644 spine/device_local.go delete mode 100644 spine/device_local_test.go delete mode 100644 spine/device_remote.go delete mode 100644 spine/device_remote_test.go delete mode 100644 spine/entity.go delete mode 100644 spine/entity_local.go delete mode 100644 spine/entity_local_test.go delete mode 100644 spine/entity_remote.go delete mode 100644 spine/events.go delete mode 100644 spine/feature.go delete mode 100644 spine/feature_local.go delete mode 100644 spine/feature_local_test.go delete mode 100644 spine/feature_remote.go delete mode 100644 spine/function_data.go delete mode 100644 spine/function_data_cmd.go delete mode 100644 spine/function_data_cmd_test.go delete mode 100644 spine/function_data_factory.go delete mode 100644 spine/function_data_factory_test.go delete mode 100644 spine/function_data_test.go delete mode 100644 spine/heartbeat_manager.go delete mode 100644 spine/heartbeat_manager_test.go delete mode 100644 spine/helper_test.go delete mode 100644 spine/message.go delete mode 100644 spine/mocks/Sender.go delete mode 100644 spine/mocks/SpineDataConnection.go delete mode 100644 spine/model/actuatorlevel.go delete mode 100644 spine/model/actuatorswitch.go delete mode 100644 spine/model/alarm.go delete mode 100644 spine/model/alarm_additions.go delete mode 100644 spine/model/alarm_additions_test.go delete mode 100644 spine/model/bill.go delete mode 100644 spine/model/bill_additions.go delete mode 100644 spine/model/bill_additions_test.go delete mode 100644 spine/model/bindingmanagement.go delete mode 100644 spine/model/bindingmanagement_additions.go delete mode 100644 spine/model/bindingmanagement_additions_test.go delete mode 100644 spine/model/collection_operations.go delete mode 100644 spine/model/collection_operations_test.go delete mode 100644 spine/model/commandframe.go delete mode 100644 spine/model/commandframe_additions.go delete mode 100644 spine/model/commandframe_additions_test.go delete mode 100644 spine/model/commondatatypes.go delete mode 100644 spine/model/commondatatypes_additions.go delete mode 100644 spine/model/commondatatypes_additions_test.go delete mode 100644 spine/model/custom.go delete mode 100644 spine/model/custom_test.go delete mode 100644 spine/model/datagram.go delete mode 100644 spine/model/datagram_additions.go delete mode 100644 spine/model/datagram_additions_test.go delete mode 100644 spine/model/datatunneling.go delete mode 100644 spine/model/deviceclassification.go delete mode 100644 spine/model/deviceconfiguration.go delete mode 100644 spine/model/deviceconfiguration_additions.go delete mode 100644 spine/model/deviceconfiguration_additions_test.go delete mode 100644 spine/model/devicediagnosis.go delete mode 100644 spine/model/directcontrol.go delete mode 100644 spine/model/eebus_tags.go delete mode 100644 spine/model/electricalconnection.go delete mode 100644 spine/model/electricalconnection_additions.go delete mode 100644 spine/model/electricalconnection_additions_test.go delete mode 100644 spine/model/hvac.go delete mode 100644 spine/model/hvac_additions.go delete mode 100644 spine/model/hvac_additions_test.go delete mode 100644 spine/model/identification.go delete mode 100644 spine/model/identification_additions.go delete mode 100644 spine/model/identification_additions_test.go delete mode 100644 spine/model/incentivetable.go delete mode 100644 spine/model/loadcontrol.go delete mode 100644 spine/model/loadcontrol_additions.go delete mode 100644 spine/model/loadcontrol_additions_test.go delete mode 100644 spine/model/measurement.go delete mode 100644 spine/model/measurement_additions.go delete mode 100644 spine/model/measurement_additions_test.go delete mode 100644 spine/model/messaging.go delete mode 100644 spine/model/messaging_additions.go delete mode 100644 spine/model/messaging_additions_test.go delete mode 100644 spine/model/networkmanagement.go delete mode 100644 spine/model/networkmanagement_additions.go delete mode 100644 spine/model/nodemanagement.go delete mode 100644 spine/model/nodemanagement_additions.go delete mode 100644 spine/model/nodemanagement_additions_test.go delete mode 100644 spine/model/operatingconstraints.go delete mode 100644 spine/model/operatingconstraints_additions.go delete mode 100644 spine/model/operatingconstraints_additions_test.go delete mode 100644 spine/model/powersequences.go delete mode 100644 spine/model/powersequences_additions.go delete mode 100644 spine/model/powersequences_additions_test.go delete mode 100644 spine/model/result.go delete mode 100644 spine/model/sensing.go delete mode 100644 spine/model/sensing_additions.go delete mode 100644 spine/model/setpoint.go delete mode 100644 spine/model/setpoint_additions.go delete mode 100644 spine/model/setpoint_additions_test.go delete mode 100644 spine/model/smartenergymanagementps.go delete mode 100644 spine/model/stateinformation.go delete mode 100644 spine/model/stateinformation_additions.go delete mode 100644 spine/model/subscriptionmanagement.go delete mode 100644 spine/model/subscriptionmanagement_additions.go delete mode 100644 spine/model/subscriptionmanagement_additions_test.go delete mode 100644 spine/model/supplyconditions.go delete mode 100644 spine/model/supplyconditions_additions.go delete mode 100644 spine/model/supplyconditions_additions_test.go delete mode 100644 spine/model/tariffinformation.go delete mode 100644 spine/model/tariffinformation_additions.go delete mode 100644 spine/model/tariffinformation_additions_test.go delete mode 100644 spine/model/taskmanagement.go delete mode 100644 spine/model/taskmanagement_additions.go delete mode 100644 spine/model/taskmanagement_additions_test.go delete mode 100644 spine/model/threshold.go delete mode 100644 spine/model/threshold_additions.go delete mode 100644 spine/model/threshold_additions_test.go delete mode 100644 spine/model/timeinformation.go delete mode 100644 spine/model/timeseries.go delete mode 100644 spine/model/timeseries_additions.go delete mode 100644 spine/model/timeseries_additions_test.go delete mode 100644 spine/model/timetable.go delete mode 100644 spine/model/timetable_additions.go delete mode 100644 spine/model/timetable_additions_test.go delete mode 100644 spine/model/update.go delete mode 100644 spine/model/update_test.go delete mode 100644 spine/model/usecaseinformation.go delete mode 100644 spine/model/usecaseinformation_additions.go delete mode 100644 spine/model/usecaseinformation_additions_test.go delete mode 100644 spine/model/version.go delete mode 100644 spine/model/version_additions.go delete mode 100644 spine/model/version_additions_test.go delete mode 100644 spine/nodemanagement.go delete mode 100644 spine/nodemanagement_binding.go delete mode 100644 spine/nodemanagement_destinationlist.go delete mode 100644 spine/nodemanagement_detaileddiscovery.go delete mode 100644 spine/nodemanagement_detaileddiscovery_test.go delete mode 100644 spine/nodemanagement_subscription.go delete mode 100644 spine/nodemanagement_test.go delete mode 100644 spine/nodemanagement_usecase.go delete mode 100644 spine/operations.go delete mode 100644 spine/operations_test.go delete mode 100644 spine/pending_requests.go delete mode 100644 spine/pending_requests_test.go delete mode 100644 spine/send.go delete mode 100644 spine/send_test.go delete mode 100644 spine/subscription_manager.go delete mode 100644 spine/subscription_manager_test.go delete mode 100644 spine/testdata/nm_destinationListData_recv_read.json delete mode 100644 spine/testdata/nm_destinationListData_send_reply_expected.json delete mode 100644 spine/testdata/nm_detaileddiscoverydata_recv_read.json delete mode 100644 spine/testdata/nm_detaileddiscoverydata_recv_read_ack.json delete mode 100644 spine/testdata/nm_detaileddiscoverydata_send_read_expected.json delete mode 100644 spine/testdata/nm_detaileddiscoverydata_send_reply_expected.json delete mode 100644 spine/testdata/nm_detaileddiscoverydata_send_result_expected.json delete mode 100644 spine/testdata/nm_subscriptionRequestCall_recv_call.json delete mode 100644 spine/testdata/nm_subscriptionRequestCall_send_result_expected.json delete mode 100644 spine/testdata/nm_usecaseinformationlistdata_recv_reply.json delete mode 100644 util/type.go delete mode 100644 util/zero.go diff --git a/LICENSE b/LICENSE index 311758a5..34e447e7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT license Copyright (c) 2022 Andreas Linde & Timo Vogel +Copyright (c) 2023-2024 Andreas Linde Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a705e47f..71392bc7 100644 --- a/README.md +++ b/README.md @@ -5,24 +5,21 @@ [![Coverage Status](https://coveralls.io/repos/github/enbility/eebus-go/badge.svg?branch=dev)](https://coveralls.io/github/enbility/eebus-go?branch=dev) [![Go report](https://goreportcard.com/badge/github.com/enbility/eebus-go)](https://goreportcard.com/report/github.com/enbility/eebus-go) -This library provides a complete foundation for implementing [EEBUS](https://eebus.org) use cases. The use cases define various functional scenarios for different device categories, e.g. energy management systems, charging stations, heat pumps, and more. +This library provides a foundation for implementing [EEBUS](https://eebus.org) use cases in [go](https://golang.org). It uses the SHIP implementation [ship-go](https://github.com/enbility/ship-go) and the SPINE implementation [spine-go](https://github.com/enbility/spine-go). Both repositories started as part of this repository, before they were moved into their own separate repositories and go packages. + +Basic understanding of the EEBUS concepts SHIP and SPINE to use this library is required. Please check the corresponding specifications on the [EEBUS downloads website](https://www.eebus.org/media-downloads/). ## Introduction The supported functionality contains: -- Support for SHIP 1.0.1 -- Support for big parts of SPINE 1.1.1 -- (De-)serialization for EEBUS specific JSON format requirements +- Support for SHIP 1.0.1 via [ship-go](https://github.com/enbility/ship-go) +- Support for SPINE 1.3.0 via [spine-go](https://github.com/enbility/spine-go) - Certificate handling - mDNS Support, incl. avahi support (recommended) - Connection (websocket) handling, including reconnection and double connections - Support for handling pairing of devices -Basic understanding of the EEBUS concepts SHIP and SPINE to use this library is required. Please check the corresponding specifications on the [EEBUS downloads website](https://www.eebus.org/media-downloads/). - -An open source SDK written in go providing the foundation to use EEBUS in your projects. Contains support for SHIP and SPINE communication. - ## Usage The included small demo applications do not implement any usecases and thus will end the connection once it reached exchanging usecase information. @@ -95,151 +92,12 @@ This approach has been tested with: - Porsche Mobile Charger Connect - SMA Home Energy Manager 2.0 -## Roadmap - Spine specification implementation - -### General request processing - -- [X] Request and process full data -- [ ] Request partial data - - [ ] Delete Selectors - - [ ] Update Selectors - - [ ] Elements -- [ ] Send - - [X] Full data - - [ ] Partial data -- [X] Process partial data - - [X] Delete Selectors - - [X] Update Selectors - - [X] Elements -- [ ] Request types - - [X] Read - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [ ] Partial Delete - - [X] Receive - - [X] Full Request - - [X] Partial Request - - [X] Partial Delete - - [X] Reply - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [ ] Partial Delete - - [X] Receive - - [X] Full Request - - [X] Partial Request - - [X] Partial Delete - - [X] Notify - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [ ] Partial Delete - - [X] Receive - - [X] Full Request - - [X] Partial Request - - [X] Partial Delete - - [X] Write - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [ ] Partial Delete - - [X] Receive - - [X] Full Request - - [X] Partial Request - - [X] Partial Delete -- [X] Result message handling - - [X] Handle incoming error results - - [X] Handle incoming success results - - [X] Respond with error result when processing failed -- [X] Acknowledgement support - - [X] Request - - [X] Respond -- [x] Use maximum response delay to timeout requests - -### Node Management - -- [ ] Detailed Discovery - - [ ] Read Messages - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [X] Receive - - [X] Full Request - - [X] Partial Request - - [ ] Reply Messages - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [X] Receive - - [X] Full Request - - [X] Partial Request - - [ ] Notify Messages - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [X] Receive - - [X] Full Request - - [X] Partial Request -- [ ] Destination List - - [ ] Request and process full data - - [X] Response full data - - [ ] Request and process partial data - - [ ] Response partial data - - [ ] Notify subscribers -- [ ] Binding - - [ ] Send Requests - - [X] Add Binding - - [ ] Delete Binding - - [X] Receive Requests - - [X] Add Binding - - [X] Delete Binding -- [ ] Subscription - - [ ] Send Requests - - [X] Add Subscription - - [ ] Delete Subscription - - [X] Receive Requests - - [X] Add Subscription - - [X] Delete Subscription - - [X] Notify subscribers -- [ ] Use Case Discovery - - [ ] Read Messages - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [X] Receive - - [X] Full Request - - [X] Partial Request - - [ ] Reply Messages - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [X] Receive - - [X] Full Request - - [X] Partial Request - - [ ] Notify Messages - - [ ] Send - - [X] Full Request - - [ ] Partial Request - - [X] Receive - - [X] Full Request - - [X] Partial Request - -### General feature implementation - -- [ ] Hearbeat Support - - [X] Send hearbeats - - [ ] Receive hearbeats - -### Partial, selector, elements support - -All list types do support processing of incoming partial messages, including selectors and elements. Sending partial messages is possible but there is no special support implemented right now. ## Interfaces ### Verbose logging -Use `SetLogger` on `Service` to set the logger which needs to conform to the `logging.Logging` interface. +Use `SetLogger` on `Service` to set the logger which needs to conform to the `logging.Logging` interface of [ship-go](https://github.com/enbility/ship-go). Example: diff --git a/api/.mockery.yaml b/api/.mockery.yaml new file mode 100644 index 00000000..01e7959b --- /dev/null +++ b/api/.mockery.yaml @@ -0,0 +1,9 @@ +with-expecter: True +inpackage: false +dir: ../mocks/{{ replaceAll .InterfaceDirRelative "internal" "internal_" }} +mockname: "{{.InterfaceName}}" +outpkg: "mocks" +filename: "{{.InterfaceName}}.go" +all: True +packages: + github.com/enbility/eebus-go/api: diff --git a/service/api.go b/api/api.go similarity index 67% rename from service/api.go rename to api/api.go index 98892b79..856d7030 100644 --- a/service/api.go +++ b/api/api.go @@ -1,19 +1,26 @@ -package service +package api -/* EEBUSService */ +import ( + "github.com/enbility/ship-go/logging" + + spineapi "github.com/enbility/spine-go/api" +) -//go:generate mockgen -destination=mock_service_test.go -package=service github.com/enbility/eebus-go/service EEBUSServiceHandler +// //go:generate mockery +//go:generate mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider,MdnsService -// interface for receiving data for specific events +/* EEBUSService */ + +// interface for receiving data for specific events from EEBUSService type EEBUSServiceHandler interface { // report all currently visible EEBUS services - VisibleRemoteServicesUpdated(service *EEBUSService, entries []RemoteService) + VisibleRemoteServicesUpdated(service EEBUSService, entries []RemoteService) // report a connection to a SKI - RemoteSKIConnected(service *EEBUSService, ski string) + RemoteSKIConnected(service EEBUSService, ski string) // report a disconnection to a SKI - RemoteSKIDisconnected(service *EEBUSService, ski string) + RemoteSKIDisconnected(service EEBUSService, ski string) // Provides the SHIP ID the remote service reported during the handshake process // This needs to be persisted and passed on for future remote service connections @@ -29,11 +36,28 @@ type EEBUSServiceHandler interface { AllowWaitingForTrust(ski string) bool } -/* Hub */ +type EEBUSService interface { + Setup() error + Start() + Shutdown() + SetLogging(logger logging.Logging) -//go:generate mockgen -destination=mock_hub_test.go -package=service github.com/enbility/eebus-go/service ServiceProvider,ConnectionsHub + LocalDevice() spineapi.DeviceLocal + RemoteServiceForSKI(ski string) *ServiceDetails + RegisterRemoteSKI(ski string, enable bool) + InitiatePairingWithSKI(ski string) + CancelPairingWithSKI(ski string) + DisconnectSKI(ski string, reason string) + + // Passthough functions to ConnectionsHub + PairingDetailForSki(ski string) *ConnectionStateDetail + StartBrowseMdnsEntries() + StopBrowseMdnsEntries() +} + +/* Hub */ -// interface for reporting data from connectionsHub to the EEBUSService +// interface for reporting data from connectionsHub to the Service type ServiceProvider interface { // report a newly discovered remote EEBUS service VisibleMDNSRecordsUpdated(entries []*MdnsEntry) @@ -70,8 +94,6 @@ type ConnectionsHub interface { /* Mdns */ -//go:generate mockgen -destination=mock_mdns_test.go -package=service github.com/enbility/eebus-go/service MdnsSearch,MdnsService - // implemented by hubConnection, used by mdns type MdnsSearch interface { ReportMdnsEntries(entries map[string]*MdnsEntry) diff --git a/service/types.go b/api/configuration.go similarity index 60% rename from service/types.go rename to api/configuration.go index 4b5b4a83..fa65feee 100644 --- a/service/types.go +++ b/api/configuration.go @@ -1,136 +1,15 @@ -package service +package api import ( "crypto/tls" - "errors" "fmt" - "sync" "time" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" ) const defaultPort int = 4711 -// connection state for global usage, e.g. UI -type ConnectionState uint - -const ( - ConnectionStateNone ConnectionState = iota // The initial state, when no pairing exists - ConnectionStateQueued // The connection request has been started and is pending connection initialization - ConnectionStateInitiated // This service initiated the connection process - ConnectionStateReceivedPairingRequest // A remote service initiated the connection process - ConnectionStateInProgress // The connection handshake is in progress - ConnectionStateTrusted // The connection is trusted on both ends - ConnectionStatePin // PIN processing, not supported right now! - ConnectionStateCompleted // The connection handshake is completed from both ends - ConnectionStateRemoteDeniedTrust // The remote service denied trust - ConnectionStateError // The connection handshake resulted in an error -) - -// the connection state of a service and error if applicable -type ConnectionStateDetail struct { - state ConnectionState - error error - - mux sync.Mutex -} - -func NewConnectionStateDetail(state ConnectionState, err error) *ConnectionStateDetail { - return &ConnectionStateDetail{ - state: state, - error: err, - } -} - -func (c *ConnectionStateDetail) State() ConnectionState { - c.mux.Lock() - defer c.mux.Unlock() - - return c.state -} - -func (c *ConnectionStateDetail) SetState(state ConnectionState) { - c.mux.Lock() - defer c.mux.Unlock() - - c.state = state -} - -func (c *ConnectionStateDetail) Error() error { - c.mux.Lock() - defer c.mux.Unlock() - - return c.error -} - -func (c *ConnectionStateDetail) SetError(err error) { - c.mux.Lock() - defer c.mux.Unlock() - - c.error = err -} - -// generic service details about the local or any remote service -type ServiceDetails struct { - // This is the SKI of the service - // This needs to be persisted - SKI string - - // This is the IPv4 address of the device running the service - // This is optional only needed when this runs with - // zeroconf as mDNS and the remote device is using the latest - // avahi version and thus zeroconf can sometimes not detect - // the IPv4 address and not initiate a connection - IPv4 string - - // shipID is the SHIP identifier of the service - // This needs to be persisted - ShipID string - - // The EEBUS device type of the device model - DeviceType model.DeviceTypeType - - // Flags if the service auto auto accepts other services - RegisterAutoAccept bool - - // Flags if the service is trusted and should be reconnected to - // Should be enabled after the connection process resulted - // ConnectionStateDetail == ConnectionStateTrusted the first time - Trusted bool - - // the current connection state details - connectionStateDetail *ConnectionStateDetail - - mux sync.Mutex -} - -// create a new ServiceDetails record with a SKI -func NewServiceDetails(ski string) *ServiceDetails { - connState := NewConnectionStateDetail(ConnectionStateNone, nil) - service := &ServiceDetails{ - SKI: util.NormalizeSKI(ski), // standardize the provided SKI strings - connectionStateDetail: connState, - } - - return service -} - -func (s *ServiceDetails) ConnectionStateDetail() *ConnectionStateDetail { - s.mux.Lock() - defer s.mux.Unlock() - - return s.connectionStateDetail -} - -func (s *ServiceDetails) SetConnectionStateDetail(detail *ConnectionStateDetail) { - s.mux.Lock() - defer s.mux.Unlock() - - s.connectionStateDetail = detail -} - // defines requires meta information about this service type Configuration struct { // The vendors IANA PEN, optional but highly recommended. @@ -222,6 +101,10 @@ func NewConfiguration( heartbeatTimeout: heartbeatTimeout, } + if port == 0 { + configuration.port = defaultPort + } + isRequired := "is required" if len(vendorCode) == 0 { @@ -260,6 +143,22 @@ func NewConfiguration( return configuration, nil } +func (s *Configuration) VendorCode() string { + return s.vendorCode +} + +func (s *Configuration) DeviceBrand() string { + return s.deviceBrand +} + +func (s *Configuration) DeviceModel() string { + return s.deviceModel +} + +func (s *Configuration) DeviceSerialNumber() string { + return s.deviceSerialNumber +} + // define an alternative mDNS and SHIP identifier // usually this is only used when no deviceCode is available or identical to the brand // if this is not set, generated identifier is used @@ -273,6 +172,22 @@ func (s *Configuration) SetAlternateMdnsServiceName(name string) { s.alternateMdnsServiceName = name } +func (s *Configuration) DeviceType() model.DeviceTypeType { + return s.deviceType +} + +func (s *Configuration) FeatureSet() model.NetworkManagementFeatureSetType { + return s.featureSet +} + +func (s *Configuration) EntityTypes() []model.EntityTypeType { + return s.entityTypes +} + +func (s *Configuration) Interfaces() []string { + return s.interfaces +} + // define which network interfaces should be considered instead of all existing // expects a list of network interface names func (s *Configuration) SetInterfaces(ifaces []string) { @@ -317,13 +232,27 @@ func (s *Configuration) MdnsServiceName() string { return s.generateIdentifier() } +func (s *Configuration) Certificate() tls.Certificate { + return s.certificate +} + +func (s *Configuration) Port() int { + return s.port +} + +func (s *Configuration) SetCertificate(cert tls.Certificate) { + s.certificate = cert +} + +func (s *Configuration) RegisterAutoAccept() bool { + return s.registerAutoAccept +} + // return the sites predefined grid voltage func (s *Configuration) Voltage() float64 { return s.voltage } -// ErrServiceNotPaired if the given SKI is not paired yet -var ErrServiceNotPaired = errors.New("the provided SKI is not paired") - -// ErrConnectionNotFound that there was no active connection for a given SKI found -var ErrConnectionNotFound = errors.New("no connection for provided SKI found") +func (s *Configuration) HeartbeatTimeout() time.Duration { + return s.heartbeatTimeout +} diff --git a/service/types_test.go b/api/configuration_test.go similarity index 51% rename from service/types_test.go rename to api/configuration_test.go index 29ee5de7..7d1d8cee 100644 --- a/service/types_test.go +++ b/api/configuration_test.go @@ -1,55 +1,25 @@ -package service +package api import ( - "errors" "testing" "time" - spineModel "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/cert" + spinemodel "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) -func TestTypesSuite(t *testing.T) { - suite.Run(t, new(TypesSuite)) +func TestConfigurationSuite(t *testing.T) { + suite.Run(t, new(ConfigurationSuite)) } -type TypesSuite struct { +type ConfigurationSuite struct { suite.Suite } -func (s *TypesSuite) SetupSuite() {} -func (s *TypesSuite) TearDownTest() {} - -func (s *TypesSuite) BeforeTest(suiteName, testName string) {} - -func (s *TypesSuite) Test_ConnectionState() { - conState := NewConnectionStateDetail(ConnectionStateNone, nil) - assert.Equal(s.T(), ConnectionStateNone, conState.State()) - assert.Nil(s.T(), conState.Error()) - - conState.SetState(ConnectionStateError) - assert.Equal(s.T(), ConnectionStateError, conState.State()) - - conState.SetError(errors.New("test")) - assert.NotNil(s.T(), conState.Error()) -} - -func (s *TypesSuite) Test_ServiceDetails() { - testSki := "test" - - details := NewServiceDetails(testSki) - assert.NotNil(s.T(), details) - - conState := NewConnectionStateDetail(ConnectionStateNone, nil) - details.SetConnectionStateDetail(conState) - - state := details.ConnectionStateDetail() - assert.Equal(s.T(), ConnectionStateNone, state.State()) -} - -func (s *TypesSuite) Test_Configuration() { - certificate, _ := CreateCertificate("unit", "org", "DE", "CN") +func (s *ConfigurationSuite) Test_Configuration() { + certificate, _ := cert.CreateCertificate("unit", "org", "DE", "CN") vendor := "vendor" brand := "brand" model := "model" @@ -57,44 +27,44 @@ func (s *TypesSuite) Test_Configuration() { port := 4567 volt := 230.0 - config, err := NewConfiguration("", brand, model, serial, spineModel.DeviceTypeTypeEnergyManagementSystem, - []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, volt, time.Second*4) + config, err := NewConfiguration("", brand, model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, + []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, volt, time.Second*4) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) - config, err = NewConfiguration(vendor, "", model, serial, spineModel.DeviceTypeTypeEnergyManagementSystem, - []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + config, err = NewConfiguration(vendor, "", model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, + []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) - config, err = NewConfiguration(vendor, brand, "", serial, spineModel.DeviceTypeTypeEnergyManagementSystem, - []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + config, err = NewConfiguration(vendor, brand, "", serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, + []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) - config, err = NewConfiguration(vendor, brand, model, "", spineModel.DeviceTypeTypeEnergyManagementSystem, - []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + config, err = NewConfiguration(vendor, brand, model, "", spinemodel.DeviceTypeTypeEnergyManagementSystem, + []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) config, err = NewConfiguration(vendor, brand, model, serial, "", - []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) - config, err = NewConfiguration(vendor, brand, model, serial, spineModel.DeviceTypeTypeEnergyManagementSystem, - []spineModel.EntityTypeType{}, port, certificate, 230, time.Second*4) + config, err = NewConfiguration(vendor, brand, model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, + []spinemodel.EntityTypeType{}, port, certificate, 230, time.Second*4) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) - config, err = NewConfiguration(vendor, brand, model, serial, spineModel.DeviceTypeTypeEnergyManagementSystem, - []spineModel.EntityTypeType{spineModel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + config, err = NewConfiguration(vendor, brand, model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, + []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) assert.NotNil(s.T(), config) assert.Nil(s.T(), err) diff --git a/api/connectionstate.go b/api/connectionstate.go new file mode 100644 index 00000000..8d5766c6 --- /dev/null +++ b/api/connectionstate.go @@ -0,0 +1,95 @@ +package api + +import ( + "errors" + "net" + "sync" +) + +// connection state for global usage, e.g. UI +type ConnectionState uint + +const ( + ConnectionStateNone ConnectionState = iota // The initial state, when no pairing exists + ConnectionStateQueued // The connection request has been started and is pending connection initialization + ConnectionStateInitiated // This service initiated the connection process + ConnectionStateReceivedPairingRequest // A remote service initiated the connection process + ConnectionStateInProgress // The connection handshake is in progress + ConnectionStateTrusted // The connection is trusted on both ends + ConnectionStatePin // PIN processing, not supported right now! + ConnectionStateCompleted // The connection handshake is completed from both ends + ConnectionStateRemoteDeniedTrust // The remote service denied trust + ConnectionStateError // The connection handshake resulted in an error +) + +// the connection state of a service and error if applicable +type ConnectionStateDetail struct { + state ConnectionState + error error + + mux sync.Mutex +} + +func NewConnectionStateDetail(state ConnectionState, err error) *ConnectionStateDetail { + return &ConnectionStateDetail{ + state: state, + error: err, + } +} + +func (c *ConnectionStateDetail) State() ConnectionState { + c.mux.Lock() + defer c.mux.Unlock() + + return c.state +} + +func (c *ConnectionStateDetail) SetState(state ConnectionState) { + c.mux.Lock() + defer c.mux.Unlock() + + c.state = state +} + +func (c *ConnectionStateDetail) Error() error { + c.mux.Lock() + defer c.mux.Unlock() + + return c.error +} + +func (c *ConnectionStateDetail) SetError(err error) { + c.mux.Lock() + defer c.mux.Unlock() + + c.error = err +} + +// ErrServiceNotPaired if the given SKI is not paired yet +var ErrServiceNotPaired = errors.New("the provided SKI is not paired") + +// ErrConnectionNotFound that there was no active connection for a given SKI found +var ErrConnectionNotFound = errors.New("no connection for provided SKI found") + +type RemoteService struct { + Name string `json:"name"` + Ski string `json:"ski"` + Identifier string `json:"identifier"` + Brand string `json:"brand"` + Type string `json:"type"` + Model string `json:"model"` +} + +type MdnsEntry struct { + Name string + Ski string + Identifier string // mandatory + Path string // mandatory + Register bool // mandatory + Brand string // optional + Type string // optional + Model string // optional + Host string // mandatory + Port int // mandatory + Addresses []net.IP // mandatory +} diff --git a/api/connectionstate_test.go b/api/connectionstate_test.go new file mode 100644 index 00000000..583adb23 --- /dev/null +++ b/api/connectionstate_test.go @@ -0,0 +1,29 @@ +package api + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestTypesSuite(t *testing.T) { + suite.Run(t, new(TypesSuite)) +} + +type TypesSuite struct { + suite.Suite +} + +func (s *TypesSuite) Test_ConnectionState() { + conState := NewConnectionStateDetail(ConnectionStateNone, nil) + assert.Equal(s.T(), ConnectionStateNone, conState.State()) + assert.Nil(s.T(), conState.Error()) + + conState.SetState(ConnectionStateError) + assert.Equal(s.T(), ConnectionStateError, conState.State()) + + conState.SetError(errors.New("test")) + assert.NotNil(s.T(), conState.Error()) +} diff --git a/api/servicedetails.go b/api/servicedetails.go new file mode 100644 index 00000000..86bc9fb9 --- /dev/null +++ b/api/servicedetails.go @@ -0,0 +1,67 @@ +package api + +import ( + "sync" + + "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" +) + +// generic service details about the local or any remote service +type ServiceDetails struct { + // This is the SKI of the service + // This needs to be persisted + SKI string + + // This is the IPv4 address of the device running the service + // This is optional only needed when this runs with + // zeroconf as mDNS and the remote device is using the latest + // avahi version and thus zeroconf can sometimes not detect + // the IPv4 address and not initiate a connection + IPv4 string + + // shipID is the SHIP identifier of the service + // This needs to be persisted + ShipID string + + // The EEBUS device type of the device model + DeviceType model.DeviceTypeType + + // Flags if the service auto auto accepts other services + RegisterAutoAccept bool + + // Flags if the service is trusted and should be reconnected to + // Should be enabled after the connection process resulted + // ConnectionStateDetail == ConnectionStateTrusted the first time + Trusted bool + + // the current connection state details + connectionStateDetail *ConnectionStateDetail + + mux sync.Mutex +} + +// create a new ServiceDetails record with a SKI +func NewServiceDetails(ski string) *ServiceDetails { + connState := NewConnectionStateDetail(ConnectionStateNone, nil) + service := &ServiceDetails{ + SKI: util.NormalizeSKI(ski), // standardize the provided SKI strings + connectionStateDetail: connState, + } + + return service +} + +func (s *ServiceDetails) ConnectionStateDetail() *ConnectionStateDetail { + s.mux.Lock() + defer s.mux.Unlock() + + return s.connectionStateDetail +} + +func (s *ServiceDetails) SetConnectionStateDetail(detail *ConnectionStateDetail) { + s.mux.Lock() + defer s.mux.Unlock() + + s.connectionStateDetail = detail +} diff --git a/api/servicedetails_test.go b/api/servicedetails_test.go new file mode 100644 index 00000000..a84f448b --- /dev/null +++ b/api/servicedetails_test.go @@ -0,0 +1,29 @@ +package api + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestServiceDetails(t *testing.T) { + suite.Run(t, new(ServiceDetailsSuite)) +} + +type ServiceDetailsSuite struct { + suite.Suite +} + +func (s *ServiceDetailsSuite) Test_ServiceDetails() { + testSki := "test" + + details := NewServiceDetails(testSki) + assert.NotNil(s.T(), details) + + conState := NewConnectionStateDetail(ConnectionStateNone, nil) + details.SetConnectionStateDetail(conState) + + state := details.ConnectionStateDetail() + assert.Equal(s.T(), ConnectionStateNone, state.State()) +} diff --git a/service/cert.go b/cert/cert.go similarity index 96% rename from service/cert.go rename to cert/cert.go index 7334cd4d..e08229c1 100644 --- a/service/cert.go +++ b/cert/cert.go @@ -1,4 +1,4 @@ -package service +package cert import ( "crypto/ecdsa" @@ -14,7 +14,7 @@ import ( "time" ) -var ciperSuites = []uint16{ +var CiperSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, // SHIP 9.1: required cipher suite tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // SHIP 9.1: optional cipher suite } @@ -80,7 +80,7 @@ func CreateCertificate(organizationalUnit, organization, country, commonName str return tlsCertificate, nil } -func skiFromCertificate(cert *x509.Certificate) (string, error) { +func SkiFromCertificate(cert *x509.Certificate) (string, error) { // check if the clients certificate provides a SKI subjectKeyId := cert.SubjectKeyId if len(subjectKeyId) != 20 { diff --git a/service/cert_test.go b/cert/cert_test.go similarity index 92% rename from service/cert_test.go rename to cert/cert_test.go index fc61bc31..73f90d8a 100644 --- a/service/cert_test.go +++ b/cert/cert_test.go @@ -1,4 +1,4 @@ -package service +package cert import ( "crypto/ecdsa" @@ -37,22 +37,22 @@ func (c *CertSuite) Test_SkiFromCertificate() { leaf, err := x509.ParseCertificate(cert.Certificate[0]) assert.Nil(c.T(), err) - ski, err := skiFromCertificate(leaf) + ski, err := SkiFromCertificate(leaf) assert.Nil(c.T(), err) assert.NotEqual(c.T(), "", ski) - cert, err = CreateInvalidCertificate("unit", "org", "DE", "CN") + cert, err = createInvalidCertificate("unit", "org", "DE", "CN") assert.Nil(c.T(), err) leaf, err = x509.ParseCertificate(cert.Certificate[0]) assert.Nil(c.T(), err) - ski, err = skiFromCertificate(leaf) + ski, err = SkiFromCertificate(leaf) assert.NotNil(c.T(), err) assert.Equal(c.T(), "", ski) } -func CreateInvalidCertificate(organizationalUnit, organization, country, commonName string) (tls.Certificate, error) { +func createInvalidCertificate(organizationalUnit, organization, country, commonName string) (tls.Certificate, error) { privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return tls.Certificate{}, err diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 6d8440c2..4c975faa 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -13,14 +13,16 @@ import ( "syscall" "time" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/model" ) var remoteSki string type evse struct { - myService *service.EEBUSService + myService *service.EEBUSServiceImpl } func (h *evse) run() { @@ -36,7 +38,7 @@ func (h *evse) run() { log.Fatal(err) } } else { - certificate, err = service.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-02") + certificate, err = cert.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-02") if err != nil { log.Fatal(err) } @@ -61,7 +63,7 @@ func (h *evse) run() { log.Fatal(err) } - configuration, err := service.NewConfiguration( + configuration, err := api.NewConfiguration( "Demo", "Demo", "EVSE", "234567890", model.DeviceTypeTypeChargingStation, []model.EntityTypeType{model.EntityTypeTypeEVSE}, @@ -91,17 +93,17 @@ func (h *evse) run() { // EEBUSServiceHandler -func (h *evse) RemoteSKIConnected(service *service.EEBUSService, ski string) {} +func (h *evse) RemoteSKIConnected(service api.EEBUSService, ski string) {} -func (h *evse) RemoteSKIDisconnected(service *service.EEBUSService, ski string) {} +func (h *evse) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} -func (h *evse) VisibleRemoteServicesUpdated(service *service.EEBUSService, entries []service.RemoteService) { +func (h *evse) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []api.RemoteService) { } func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *evse) ServicePairingDetailUpdate(ski string, detail *service.ConnectionStateDetail) { - if ski == remoteSki && detail.State() == service.ConnectionStateRemoteDeniedTrust { +func (h *evse) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { + if ski == remoteSki && detail.State() == api.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") h.myService.RegisterRemoteSKI(ski, false) h.myService.CancelPairingWithSKI(ski) diff --git a/cmd/hems/main.go b/cmd/hems/main.go index 54f49bf2..a8822158 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -13,14 +13,16 @@ import ( "syscall" "time" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/model" ) var remoteSki string type hems struct { - myService *service.EEBUSService + myService *service.EEBUSServiceImpl } func (h *hems) run() { @@ -36,7 +38,7 @@ func (h *hems) run() { log.Fatal(err) } } else { - certificate, err = service.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-01") + certificate, err = cert.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-01") if err != nil { log.Fatal(err) } @@ -61,7 +63,7 @@ func (h *hems) run() { log.Fatal(err) } - configuration, err := service.NewConfiguration( + configuration, err := api.NewConfiguration( "Demo", "Demo", "HEMS", "123456789", model.DeviceTypeTypeEnergyManagementSystem, []model.EntityTypeType{model.EntityTypeTypeCEM}, @@ -91,17 +93,17 @@ func (h *hems) run() { // EEBUSServiceHandler -func (h *hems) RemoteSKIConnected(service *service.EEBUSService, ski string) {} +func (h *hems) RemoteSKIConnected(service api.EEBUSService, ski string) {} -func (h *hems) RemoteSKIDisconnected(service *service.EEBUSService, ski string) {} +func (h *hems) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} -func (h *hems) VisibleRemoteServicesUpdated(service *service.EEBUSService, entries []service.RemoteService) { +func (h *hems) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []api.RemoteService) { } func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *hems) ServicePairingDetailUpdate(ski string, detail *service.ConnectionStateDetail) { - if ski == remoteSki && detail.State() == service.ConnectionStateRemoteDeniedTrust { +func (h *hems) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { + if ski == remoteSki && detail.State() == api.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") h.myService.RegisterRemoteSKI(ski, false) h.myService.CancelPairingWithSKI(ski) diff --git a/features/api.go b/features/api.go index 666937ac..53797d71 100644 --- a/features/api.go +++ b/features/api.go @@ -1,11 +1,11 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type Feature interface { SubscribeForEntity() error - AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg spine.ResultMessage)) + AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) } diff --git a/features/deviceclassification.go b/features/deviceclassification.go index 7ff22816..49b3f82b 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type DeviceClassification struct { *FeatureImpl } -func NewDeviceClassification(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*DeviceClassification, error) { +func NewDeviceClassification(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*DeviceClassification, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/deviceclassification_test.go b/features/deviceclassification_test.go index 7067355c..7423b328 100644 --- a/features/deviceclassification_test.go +++ b/features/deviceclassification_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -19,14 +19,14 @@ func TestDeviceClassificationSuite(t *testing.T) { type DeviceClassificationSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote deviceClassification *features.DeviceClassification sentMessage []byte } -var _ ship.SpineDataConnection = (*DeviceClassificationSuite)(nil) +var _ shipapi.SpineDataConnection = (*DeviceClassificationSuite)(nil) func (s *DeviceClassificationSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index 4fee3a66..6db80aa4 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type DeviceConfiguration struct { *FeatureImpl } -func NewDeviceConfiguration(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*DeviceConfiguration, error) { +func NewDeviceConfiguration(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*DeviceConfiguration, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index be7c459c..9c90bccf 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -19,14 +19,14 @@ func TestDeviceConfigurationSuite(t *testing.T) { type DeviceConfigurationSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote deviceConfiguration *features.DeviceConfiguration sentMessage []byte } -var _ ship.SpineDataConnection = (*DeviceConfigurationSuite)(nil) +var _ shipapi.SpineDataConnection = (*DeviceConfigurationSuite)(nil) func (s *DeviceConfigurationSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index 92a8032c..d3af75ea 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type DeviceDiagnosis struct { *FeatureImpl } -func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*DeviceDiagnosis, error) { +func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*DeviceDiagnosis, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index c58ce044..1dbcb064 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -19,14 +19,14 @@ func TestDeviceDiagnosisSuite(t *testing.T) { type DeviceDiagnosisSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote deviceDiagnosis *features.DeviceDiagnosis sentMessage []byte } -var _ ship.SpineDataConnection = (*DeviceDiagnosisSuite)(nil) +var _ shipapi.SpineDataConnection = (*DeviceDiagnosisSuite)(nil) func (s *DeviceDiagnosisSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/electricalconnection.go b/features/electricalconnection.go index a8afedf7..680fc435 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type ElectricalConnection struct { *FeatureImpl } -func NewElectricalConnection(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*ElectricalConnection, error) { +func NewElectricalConnection(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*ElectricalConnection, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 20ea5284..11a54453 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -19,14 +19,14 @@ func TestElectricalConnectionSuite(t *testing.T) { type ElectricalConnectionSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote electricalConnection *features.ElectricalConnection sentMessage []byte } -var _ ship.SpineDataConnection = (*ElectricalConnectionSuite)(nil) +var _ shipapi.SpineDataConnection = (*ElectricalConnectionSuite)(nil) func (s *ElectricalConnectionSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/feature.go b/features/feature.go index bb04b416..55add157 100644 --- a/features/feature.go +++ b/features/feature.go @@ -3,8 +3,8 @@ package features import ( "errors" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type FeatureImpl struct { @@ -13,19 +13,19 @@ type FeatureImpl struct { localRole model.RoleType remoteRole model.RoleType - spineLocalDevice spine.DeviceLocal - localEntity spine.EntityLocal + spineLocalDevice api.DeviceLocal + localEntity api.EntityLocal - featureLocal spine.FeatureLocal - featureRemote spine.FeatureRemote + featureLocal api.FeatureLocal + featureRemote api.FeatureRemote - remoteDevice spine.DeviceRemote - remoteEntity spine.EntityRemote + remoteDevice api.DeviceRemote + remoteEntity api.EntityRemote } var _ Feature = (*FeatureImpl)(nil) -func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*FeatureImpl, error) { +func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*FeatureImpl, error) { f := &FeatureImpl{ featureType: featureType, localRole: localRole, @@ -51,7 +51,7 @@ func (f *FeatureImpl) SubscribeForEntity() error { return nil } -func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg spine.ResultMessage)) { +func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) { f.featureLocal.AddResultCallback(msgCounterReference, function) } @@ -78,7 +78,7 @@ func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, el return nil, ErrFunctionNotSupported } - if !fTypes[function].Read { + if !fTypes[function].Read() { return nil, ErrOperationOnFunctionNotSupported } @@ -91,7 +91,7 @@ func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, el } // internal helper method for getting local and remote feature for a given featureType and a given remoteDevice -func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (spine.FeatureLocal, spine.FeatureRemote, error) { +func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (api.FeatureLocal, api.FeatureRemote, error) { if f.remoteEntity == nil { return nil, nil, errors.New("invalid remote entity provided") } diff --git a/features/helper_test.go b/features/helper_test.go index bcfa2367..e3273009 100644 --- a/features/helper_test.go +++ b/features/helper_test.go @@ -5,10 +5,11 @@ import ( "sync" "time" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" "github.com/stretchr/testify/assert" ) @@ -23,7 +24,7 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ ship.SpineDataConnection = (*WriteMessageHandler)(nil) +var _ shipapi.SpineDataConnection = (*WriteMessageHandler)(nil) func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { t.mux.Lock() @@ -95,7 +96,7 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg return nil } -func setupFeatures(t assert.TestingT, dataCon ship.SpineDataConnection, featureFunctions []featureFunctions) (spine.EntityLocal, spine.EntityRemote) { +func setupFeatures(t assert.TestingT, dataCon shipapi.SpineDataConnection, featureFunctions []featureFunctions) (spineapi.EntityLocal, spineapi.EntityRemote) { localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) diff --git a/features/identification.go b/features/identification.go index 8200460e..226efc64 100644 --- a/features/identification.go +++ b/features/identification.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type Identification struct { *FeatureImpl } -func NewIdentification(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*Identification, error) { +func NewIdentification(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*Identification, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeIdentification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/identification_test.go b/features/identification_test.go index a784955f..71048530 100644 --- a/features/identification_test.go +++ b/features/identification_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -19,14 +19,14 @@ func TestIdentificationSuite(t *testing.T) { type IdentificationSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote identification *features.Identification sentMessage []byte } -var _ ship.SpineDataConnection = (*IdentificationSuite)(nil) +var _ shipapi.SpineDataConnection = (*IdentificationSuite)(nil) func (s *IdentificationSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/incentivetable.go b/features/incentivetable.go index fe65ec3e..f1b4a633 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type IncentiveTable struct { *FeatureImpl } -func NewIncentiveTable(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*IncentiveTable, error) { +func NewIncentiveTable(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*IncentiveTable, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 7e3510d6..43a4a26b 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -19,14 +19,14 @@ func TestIncentiveTableSuite(t *testing.T) { type IncentiveTableSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote incentiveTable *features.IncentiveTable sentMessage []byte } -var _ ship.SpineDataConnection = (*IncentiveTableSuite)(nil) +var _ shipapi.SpineDataConnection = (*IncentiveTableSuite)(nil) func (s *IncentiveTableSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/loadcontrol.go b/features/loadcontrol.go index ed732920..62442c03 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type LoadControl struct { *FeatureImpl } -func NewLoadControl(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*LoadControl, error) { +func NewLoadControl(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*LoadControl, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeLoadControl, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index 212b9c71..7acde9ac 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -19,14 +19,14 @@ func TestLoadControlSuite(t *testing.T) { type LoadControlSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote loadControl *features.LoadControl sentMessage []byte } -var _ ship.SpineDataConnection = (*LoadControlSuite)(nil) +var _ shipapi.SpineDataConnection = (*LoadControlSuite)(nil) func (s *LoadControlSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/measurement.go b/features/measurement.go index a8ecc023..253361c3 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type Measurement struct { *FeatureImpl } -func NewMeasurement(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*Measurement, error) { +func NewMeasurement(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*Measurement, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeMeasurement, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/measurement_test.go b/features/measurement_test.go index 3e722b55..f969e587 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -5,10 +5,10 @@ import ( "time" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -20,14 +20,14 @@ func TestMeasurementSuite(t *testing.T) { type MeasurementSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote measurement *features.Measurement sentMessage []byte } -var _ ship.SpineDataConnection = (*MeasurementSuite)(nil) +var _ shipapi.SpineDataConnection = (*MeasurementSuite)(nil) func (s *MeasurementSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/features/timeseries.go b/features/timeseries.go index e490c81a..230cba35 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -1,15 +1,15 @@ package features import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type TimeSeries struct { *FeatureImpl } -func NewTimeSeries(localRole, remoteRole model.RoleType, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote) (*TimeSeries, error) { +func NewTimeSeries(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*TimeSeries, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeTimeSeries, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/timeseries_test.go b/features/timeseries_test.go index 4766bb79..06d9f971 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -5,10 +5,10 @@ import ( "time" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -20,14 +20,14 @@ func TestTimeSeriesSuite(t *testing.T) { type TimeSeriesSuite struct { suite.Suite - localEntity spine.EntityLocal - remoteEntity spine.EntityRemote + localEntity spineapi.EntityLocal + remoteEntity spineapi.EntityRemote timeSeries *features.TimeSeries sentMessage []byte } -var _ ship.SpineDataConnection = (*TimeSeriesSuite)(nil) +var _ shipapi.SpineDataConnection = (*TimeSeriesSuite)(nil) func (s *TimeSeriesSuite) WriteSpineMessage(message []byte) { s.sentMessage = message diff --git a/go.mod b/go.mod index 70907b48..c01fe5ae 100644 --- a/go.mod +++ b/go.mod @@ -3,32 +3,33 @@ module github.com/enbility/eebus-go go 1.18 require ( + github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df + github.com/enbility/ship-go v0.0.0-20240114193748-be8afeda7c96 + github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c + github.com/godbus/dbus/v5 v5.1.0 + github.com/gorilla/websocket v1.5.1 + github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed + github.com/stretchr/testify v1.8.4 + go.uber.org/mock v0.4.0 +) + +require ( + github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/google/go-cmp v0.6.0 + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/miekg/dns v1.1.57 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rickb777/date v1.20.5 // indirect github.com/rickb777/plural v1.4.1 // indirect github.com/stretchr/objx v0.5.1 // indirect + gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) -require ( - github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df - github.com/ahmetb/go-linq/v3 v3.2.0 - github.com/godbus/dbus/v5 v5.1.0 - github.com/gorilla/websocket v1.5.1 - github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed - github.com/rickb777/date v1.20.5 - github.com/stretchr/testify v1.8.4 - gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a - go.uber.org/mock v0.4.0 -) - retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. diff --git a/go.sum b/go.sum index 5f35559f..4be9e6b3 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,13 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/enbility/ship-go v0.0.0-20240114193748-be8afeda7c96 h1:76/ktojmXVnc58qRo93qaDwKhvxCDpvsdE3Mz9fUgv4= +github.com/enbility/ship-go v0.0.0-20240114193748-be8afeda7c96/go.mod h1:OOo/76TU3B9zhMrb0wlftKeVAvICrTTuJpM7xiRT20k= +github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= +github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -56,13 +59,13 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -72,8 +75,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -92,8 +95,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integration_tests/devicediagnosis_test.go b/integration_tests/devicediagnosis_test.go deleted file mode 100644 index 901cc73c..00000000 --- a/integration_tests/devicediagnosis_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package integrationtests - -import ( - "testing" - - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -const ( - dd_subscriptionRequestCall_recv_file_path = "./testdata/dd_subscriptionRequestCall_recv.json" - dd_subscriptionRequestCall_recv_result_file_path = "./testdata/ec_subscriptionRequestCall_recv_result.json" -) - -func TestDeviceDiagnosisSuite(t *testing.T) { - suite.Run(t, new(DeviceDiagnosisSuite)) -} - -type DeviceDiagnosisSuite struct { - suite.Suite - sut spine.DeviceLocal - - remoteSki string - - remoteDevice spine.DeviceRemote - writeHandler *WriteMessageHandler -} - -func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) { - s.sut, s.remoteSki, s.remoteDevice, s.writeHandler = beforeTest(suiteName, testName, 1, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - - // f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) - - initialCommunication(s.T(), s.remoteDevice, s.writeHandler) -} - -func (s *DeviceDiagnosisSuite) TestHeartbeatSubscription_RecvNotify() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), dd_subscriptionRequestCall_recv_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - - ddFeature := remoteDevice.FeatureByEntityTypeAndRole( - remoteDevice.Entity(spine.NewAddressEntityType([]uint{1})), - model.FeatureTypeTypeDeviceDiagnosis, - model.RoleTypeClient) - assert.NotNil(s.T(), ddFeature) -} diff --git a/integration_tests/electricalconnection_test.go b/integration_tests/electricalconnection_test.go deleted file mode 100644 index cd3477cb..00000000 --- a/integration_tests/electricalconnection_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package integrationtests - -import ( - "testing" - - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -const ( - ec_permittedvaluesetlistdata_recv_notify_partial_file_path = "./testdata/ec_permittedvaluesetlistdata_recv_notify_partial.json" - ec_descriptionlistdata_recv_reply_file_path = "./testdata/ec_descriptionListData_recv_reply.json" - ec_parameterdescriptionlistdata_recv_reply_file_path = "./testdata/ec_parameterDescriptionListData_recv_reply.json" - ec_subscriptionRequestCall_recv_result_file_path = "./testdata/ec_subscriptionRequestCall_recv_result.json" -) - -func TestElectricalConnectionSuite(t *testing.T) { - suite.Run(t, new(ElectricalConnectionSuite)) -} - -type ElectricalConnectionSuite struct { - suite.Suite - sut spine.DeviceLocal - - remoteSki string - - remoteDevice spine.DeviceRemote - writeHandler *WriteMessageHandler -} - -func (s *ElectricalConnectionSuite) SetupSuite() { -} - -func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) { - s.sut, s.remoteSki, s.remoteDevice, s.writeHandler = beforeTest(suiteName, testName, 1, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - initialCommunication(s.T(), s.remoteDevice, s.writeHandler) -} - -func (s *ElectricalConnectionSuite) AfterTest(suiteName, testName string) { -} - -func (s *ElectricalConnectionSuite) TestDescriptionListData_RecvReply() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), ec_descriptionlistdata_recv_reply_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - - ecFeature := remoteDevice.FeatureByEntityTypeAndRole( - remoteDevice.Entity(spine.NewAddressEntityType([]uint{1, 1})), - model.FeatureTypeTypeElectricalConnection, - model.RoleTypeServer) - assert.NotNil(s.T(), ecFeature) - - fdata := ecFeature.DataCopy(model.FunctionTypeElectricalConnectionDescriptionListData) - if !assert.NotNil(s.T(), fdata) { - return - } - data := fdata.(*model.ElectricalConnectionDescriptionListDataType) - - if !assert.Equal(s.T(), 1, len(data.ElectricalConnectionDescriptionData)) { - return - } - - item1 := data.ElectricalConnectionDescriptionData[0] - assert.Equal(s.T(), 0, int(*item1.ElectricalConnectionId)) - assert.Equal(s.T(), string(model.ElectricalConnectionVoltageTypeTypeAc), string(*item1.PowerSupplyType)) - assert.Equal(s.T(), 1, int(*item1.AcConnectedPhases)) - assert.Equal(s.T(), string(model.EnergyDirectionTypeConsume), string(*item1.PositiveEnergyDirection)) -} - -func (s *ElectricalConnectionSuite) TestParameterDescriptionListData_RecvReply() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), ec_parameterdescriptionlistdata_recv_reply_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - - ecFeature := remoteDevice.FeatureByEntityTypeAndRole( - remoteDevice.Entity(spine.NewAddressEntityType([]uint{1, 1})), - model.FeatureTypeTypeElectricalConnection, - model.RoleTypeServer) - assert.NotNil(s.T(), ecFeature) - - fdata := ecFeature.DataCopy(model.FunctionTypeElectricalConnectionParameterDescriptionListData) - if !assert.NotNil(s.T(), fdata) { - return - } - data := fdata.(*model.ElectricalConnectionParameterDescriptionListDataType) - - if !assert.Equal(s.T(), 4, len(data.ElectricalConnectionParameterDescriptionData)) { - return - } - - item1 := data.ElectricalConnectionParameterDescriptionData[0] - assert.Equal(s.T(), 0, int(*item1.ElectricalConnectionId)) - assert.Equal(s.T(), 1, int(*item1.ParameterId)) - assert.Equal(s.T(), 1, int(*item1.MeasurementId)) - assert.Equal(s.T(), string(model.ElectricalConnectionVoltageTypeTypeAc), string(*item1.VoltageType)) - assert.Equal(s.T(), string(model.ElectricalConnectionPhaseNameTypeA), string(*item1.AcMeasuredPhases)) - assert.Equal(s.T(), string(model.ElectricalConnectionPhaseNameTypeNeutral), string(*item1.AcMeasuredInReferenceTo)) - assert.Equal(s.T(), string(model.ElectricalConnectionAcMeasurementTypeTypeReal), string(*item1.AcMeasurementType)) - assert.Equal(s.T(), string(model.ElectricalConnectionMeasurandVariantTypeRms), string(*item1.AcMeasurementVariant)) -} - -func (s *ElectricalConnectionSuite) TestPermittedValueSetListData_RecvNotifyPartial() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), ec_permittedvaluesetlistdata_recv_notify_partial_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - - ecFeature := remoteDevice.FeatureByEntityTypeAndRole( - remoteDevice.Entity(spine.NewAddressEntityType([]uint{1, 1})), - model.FeatureTypeTypeElectricalConnection, - model.RoleTypeServer) - assert.NotNil(s.T(), ecFeature) - - fdata := ecFeature.DataCopy(model.FunctionTypeElectricalConnectionPermittedValueSetListData) - if !assert.NotNil(s.T(), fdata) { - return - } - data := fdata.(*model.ElectricalConnectionPermittedValueSetListDataType) - - if !assert.Equal(s.T(), 3, len(data.ElectricalConnectionPermittedValueSetData)) { - return - } - - item1 := data.ElectricalConnectionPermittedValueSetData[0] - assert.Equal(s.T(), 0, int(*item1.ElectricalConnectionId)) - assert.Equal(s.T(), 1, int(*item1.ParameterId)) - assert.Equal(s.T(), 1, len(item1.PermittedValueSet)) - assert.Equal(s.T(), 1, len(item1.PermittedValueSet[0].Range)) - assert.NotNil(s.T(), item1.PermittedValueSet[0].Range) - assert.Equal(s.T(), 6, int(*item1.PermittedValueSet[0].Range[0].Min.Number)) - assert.Equal(s.T(), 0, int(*item1.PermittedValueSet[0].Range[0].Min.Scale)) - assert.Equal(s.T(), 16, int(*item1.PermittedValueSet[0].Range[0].Max.Number)) - assert.Equal(s.T(), 0, int(*item1.PermittedValueSet[0].Range[0].Max.Scale)) - assert.Nil(s.T(), item1.PermittedValueSet[0].Value) -} diff --git a/integration_tests/emobility_measurement_test.go b/integration_tests/emobility_measurement_test.go index 8cd25cba..c6cd8c2d 100644 --- a/integration_tests/emobility_measurement_test.go +++ b/integration_tests/emobility_measurement_test.go @@ -5,12 +5,19 @@ import ( "time" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) +const ( + m_descriptionListData_recv_reply_file_path = "./testdata/m_descriptionListData_recv_reply.json" + m_measurementListData_recv_notify_file_path = "./testdata/m_measurementListData_recv_notify.json" + ec_parameterdescriptionlistdata_recv_reply_file_path = "./testdata/ec_parameterDescriptionListData_recv_reply.json" +) + func TestEmobilityMeasurementSuite(t *testing.T) { suite.Run(t, new(EmobilityMeasurementSuite)) } @@ -18,23 +25,19 @@ func TestEmobilityMeasurementSuite(t *testing.T) { type EmobilityMeasurementSuite struct { suite.Suite - sut spine.DeviceLocal - localEntity spine.EntityLocal + sut api.DeviceLocal + localEntity api.EntityLocal measurement *features.Measurement electricalconnection *features.ElectricalConnection remoteSki string - remoteDevice spine.DeviceRemote + remoteDevice api.DeviceRemote writeHandler *WriteMessageHandler } -func (s *EmobilityMeasurementSuite) SetupSuite() { -} - func (s *EmobilityMeasurementSuite) BeforeTest(suiteName, testName string) { - s.sut = spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) s.localEntity = spine.NewEntityLocalImpl(s.sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) @@ -54,9 +57,6 @@ func (s *EmobilityMeasurementSuite) BeforeTest(suiteName, testName string) { initialCommunication(s.T(), s.remoteDevice, s.writeHandler) } -func (s *EmobilityMeasurementSuite) AfterTest(suiteName, testName string) { -} - func (s *EmobilityMeasurementSuite) TestGetValuesPerPhaseForScope() { remoteEntity := s.sut.RemoteDeviceForSki(s.remoteSki).Entity([]model.AddressEntityType{1, 1}) assert.NotNil(s.T(), remoteEntity) diff --git a/integration_tests/helper_test.go b/integration_tests/helper_test.go index 7b486f64..2ae7de20 100644 --- a/integration_tests/helper_test.go +++ b/integration_tests/helper_test.go @@ -6,16 +6,15 @@ import ( "os" "sync" "testing" - "time" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) const ( - wallbox_detaileddiscoverydata_recv_reply_file_path = "../spine/testdata/wallbox_detaileddiscoverydata_recv_reply.json" - wallbox_detaileddiscoverydata_recv_notify_file_path = "../spine/testdata/wallbox_detaileddiscoverydata_recv_notify.json" + wallbox_detaileddiscoverydata_recv_reply_file_path = ".//testdata/wallbox_detaileddiscoverydata_recv_reply.json" + wallbox_detaileddiscoverydata_recv_notify_file_path = ".//testdata/wallbox_detaileddiscoverydata_recv_notify.json" ) type WriteMessageHandler struct { @@ -24,7 +23,7 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ ship.SpineDataConnection = (*WriteMessageHandler)(nil) +var _ shipapi.SpineDataConnection = (*WriteMessageHandler)(nil) func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { t.mux.Lock() @@ -96,26 +95,7 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg return nil } -func beforeTest( - suiteName, testName string, fId uint, ftype model.FeatureTypeType, - frole model.RoleType) (spine.DeviceLocal, string, spine.DeviceRemote, *WriteMessageHandler) { - sut := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := spine.NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) - sut.AddEntity(localEntity) - f := spine.NewFeatureLocalImpl(fId, localEntity, ftype, frole) - localEntity.AddFeature(f) - - remoteSki := "TestRemoteSki" - - writeHandler := &WriteMessageHandler{} - _ = sut.SetupRemoteDevice(remoteSki, writeHandler) - remoteDevice := sut.RemoteDeviceForSki(remoteSki) - - return sut, remoteSki, remoteDevice, writeHandler -} - -func initialCommunication(t *testing.T, remoteDevice spine.DeviceRemote, writeHandler *WriteMessageHandler) { +func initialCommunication(t *testing.T, remoteDevice spineapi.DeviceRemote, writeHandler *WriteMessageHandler) { // Initial generic communication _, _ = remoteDevice.HandleSpineMesssage(loadFileData(t, wallbox_detaileddiscoverydata_recv_reply_file_path)) diff --git a/integration_tests/measurement_test.go b/integration_tests/measurement_test.go deleted file mode 100644 index f1a7967f..00000000 --- a/integration_tests/measurement_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package integrationtests - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -const ( - m_subscriptionRequestCall_recv_result_file_path = "./testdata/m_subscriptionRequestCall_recv_result.json" - m_descriptionListData_recv_reply_file_path = "./testdata/m_descriptionListData_recv_reply.json" - m_measurementListData_recv_notify_file_path = "./testdata/m_measurementListData_recv_notify.json" -) - -func TestMeasurementSuite(t *testing.T) { - suite.Run(t, new(MeasurementSuite)) -} - -type MeasurementSuite struct { - suite.Suite - sut spine.DeviceLocal - - remoteSki string - - remoteDevice spine.DeviceRemote - writeHandler *WriteMessageHandler -} - -func (s *MeasurementSuite) SetupSuite() { -} - -func (s *MeasurementSuite) BeforeTest(suiteName, testName string) { - s.sut, s.remoteSki, s.remoteDevice, s.writeHandler = beforeTest(suiteName, testName, 2, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - initialCommunication(s.T(), s.remoteDevice, s.writeHandler) -} - -func (s *MeasurementSuite) AfterTest(suiteName, testName string) { -} - -func (s *MeasurementSuite) TestDescriptionList_Recv() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_descriptionListData_recv_reply_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - - mFeature := remoteDevice.FeatureByEntityTypeAndRole( - remoteDevice.Entity(spine.NewAddressEntityType([]uint{1, 1})), - model.FeatureTypeTypeMeasurement, - model.RoleTypeServer) - assert.NotNil(s.T(), mFeature) - - fdata := mFeature.DataCopy(model.FunctionTypeMeasurementDescriptionListData) - if !assert.NotNil(s.T(), fdata) { - return - } - data := fdata.(*model.MeasurementDescriptionListDataType) - - if !assert.Equal(s.T(), 3, len(data.MeasurementDescriptionData)) { - return - } - - item1 := data.MeasurementDescriptionData[0] - assert.Equal(s.T(), 1, int(*item1.MeasurementId)) - assert.Equal(s.T(), string(model.MeasurementTypeTypeCurrent), string(*item1.MeasurementType)) - assert.Equal(s.T(), string(model.CommodityTypeTypeElectricity), string(*item1.CommodityType)) - assert.Equal(s.T(), string(model.UnitOfMeasurementTypeA), string(*item1.Unit)) - assert.Equal(s.T(), string(model.ScopeTypeTypeACCurrent), string(*item1.ScopeType)) -} - -func (s *MeasurementSuite) TestMeasurementList_Recv() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_measurementListData_recv_notify_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - - mFeature := remoteDevice.FeatureByEntityTypeAndRole( - remoteDevice.Entity(spine.NewAddressEntityType([]uint{1, 1})), - model.FeatureTypeTypeMeasurement, - model.RoleTypeServer) - assert.NotNil(s.T(), mFeature) - - fdata := mFeature.DataCopy(model.FunctionTypeMeasurementListData) - if !assert.NotNil(s.T(), fdata) { - return - } - data := fdata.(*model.MeasurementListDataType) - - if !assert.Equal(s.T(), 3, len(data.MeasurementData)) { - return - } - - item1 := data.MeasurementData[0] - assert.Equal(s.T(), 1, int(*item1.MeasurementId)) - assert.Equal(s.T(), string(model.MeasurementValueTypeTypeValue), string(*item1.ValueType)) - assert.Equal(s.T(), 5.0, item1.Value.GetValue()) - timestamp, err := item1.Timestamp.GetDateTimeType().GetTime() - assert.Nil(s.T(), err) - compareTimestamp := time.Date( - 2022, 11, 19, 15, 21, 50, 3000000, time.UTC) - assert.Equal(s.T(), compareTimestamp, timestamp) - assert.Equal(s.T(), string(model.MeasurementValueSourceTypeMeasuredValue), string(*item1.ValueSource)) -} - -func (s *MeasurementSuite) TestMeasurementByScope_Recv() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_descriptionListData_recv_reply_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Act - msgCounter, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), m_measurementListData_recv_notify_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - - mFeature := remoteDevice.FeatureByEntityTypeAndRole( - remoteDevice.Entity(spine.NewAddressEntityType([]uint{1, 1})), - model.FeatureTypeTypeMeasurement, - model.RoleTypeServer) - assert.NotNil(s.T(), mFeature) - - fdata := mFeature.DataCopy(model.FunctionTypeMeasurementDescriptionListData) - if !assert.NotNil(s.T(), fdata) { - return - } - descData := fdata.(*model.MeasurementDescriptionListDataType) - - if !assert.Equal(s.T(), 3, len(descData.MeasurementDescriptionData)) { - return - } - - fdata = mFeature.DataCopy(model.FunctionTypeMeasurementListData) - if !assert.NotNil(s.T(), fdata) { - return - } - mData := fdata.(*model.MeasurementListDataType) - - if !assert.Equal(s.T(), 3, len(mData.MeasurementData)) { - return - } - - item1 := mData.MeasurementData[0] - assert.Equal(s.T(), 1, int(*item1.MeasurementId)) - assert.Equal(s.T(), string(model.MeasurementValueTypeTypeValue), string(*item1.ValueType)) - assert.Equal(s.T(), 5.0, item1.Value.GetValue()) - timestamp, err := item1.Timestamp.GetDateTimeType().GetTime() - assert.Nil(s.T(), err) - compareTimestamp := time.Date( - 2022, 11, 19, 15, 21, 50, 3000000, time.UTC) - assert.Equal(s.T(), compareTimestamp, timestamp) - assert.Equal(s.T(), string(model.MeasurementValueSourceTypeMeasuredValue), string(*item1.ValueSource)) -} diff --git a/integration_tests/testdata/dd_subscriptionRequestCall_recv.json b/integration_tests/testdata/dd_subscriptionRequestCall_recv.json deleted file mode 100644 index 599641ab..00000000 --- a/integration_tests/testdata/dd_subscriptionRequestCall_recv.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.1.1", - "addressSource": { - "device": "Wallbox", - "entity": [1], - "feature": 2 - }, - "addressDestination": { - "device": "HEMS", - "entity": [0], - "feature": 0 - }, - "msgCounter": 1, - "cmdClassifier": "call", - "ackRequest": true - }, - "payload": { - "cmd": [ - { - "nodeManagementSubscriptionRequestCall": { - "subscriptionRequest": { - "clientAddress": { - "device": "Wallbox", - "entity": [1], - "feature": 2 - }, - "serverAddress": { - "device": "HEMS", - "entity": [1], - "feature": 1 - }, - "serverFeatureType": "DeviceDiagnosis" - } - } - } - ] - } - } -} diff --git a/integration_tests/testdata/ec_descriptionListData_recv_reply.json b/integration_tests/testdata/ec_descriptionListData_recv_reply.json deleted file mode 100644 index 12e06b04..00000000 --- a/integration_tests/testdata/ec_descriptionListData_recv_reply.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.1.1", - "addressSource": { - "device": "Wallbox", - "entity": [1,1], - "feature": 7 - }, - "addressDestination": { - "device": "HEMS", - "entity": [1], - "feature": 1 - }, - "msgCounter": 10, - "msgCounterReference": 10, - "cmdClassifier": "reply", - "ackRequest": true - }, - "payload": { - "cmd": [ - { - "electricalConnectionDescriptionListData": { - "electricalConnectionDescriptionData": [ - { - "electricalConnectionId": 0, - "powerSupplyType": "ac", - "acConnectedPhases": 1, - "positiveEnergyDirection": "consume" - } - ] - } - } - ] - } - } -} diff --git a/integration_tests/testdata/ec_permittedvaluesetlistdata_recv_notify_partial.json b/integration_tests/testdata/ec_permittedvaluesetlistdata_recv_notify_partial.json deleted file mode 100644 index e7fe307a..00000000 --- a/integration_tests/testdata/ec_permittedvaluesetlistdata_recv_notify_partial.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.1.1", - "addressSource": { - "device": "Wallbox", - "entity": [1,1], - "feature": 7 - }, - "addressDestination": { - "device": "HEMS", - "entity": [1], - "feature": 1 - }, - "msgCounter": 10, - "cmdClassifier": "notify", - "ackRequest":true - }, - "payload": { - "cmd": [ - { - "function":"electricalConnectionPermittedValueSetListData", - "filter":[ - { - "cmdControl":{ - "partial":{} - } - } - ], - "electricalConnectionPermittedValueSetListData": { - "electricalConnectionPermittedValueSetData": [ - { - "electricalConnectionId": 0, - "parameterId": 1, - "permittedValueSet": [ - { - "range": [ - { - "min": { - "number": 6, - "scale": 0 - }, - "max": { - "number": 16, - "scale": 0 - } - } - ] - } - ] - }, - { - "electricalConnectionId": 0, - "parameterId": 2, - "permittedValueSet": [ - { - "range": [ - { - "min": { - "number": 6, - "scale": 0 - }, - "max": { - "number": 16, - "scale": 0 - } - } - ] - } - ] - }, - { - "electricalConnectionId": 0, - "parameterId": 3, - "permittedValueSet": [ - { - "range": [ - { - "min": { - "number": 6, - "scale": 0 - }, - "max": { - "number": 16, - "scale": 0 - } - } - ] - } - ] - } - ] - } - } - ] - } - } -} diff --git a/integration_tests/testdata/ec_subscriptionRequestCall_recv_result.json b/integration_tests/testdata/ec_subscriptionRequestCall_recv_result.json deleted file mode 100644 index 8186803c..00000000 --- a/integration_tests/testdata/ec_subscriptionRequestCall_recv_result.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.1.1", - "addressSource": { - "device": "Wallbox", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 3, - "msgCounterReference": 2, - "cmdClassifier": "result" - }, - "payload": { - "cmd": [ - { - "resultData": { - "errorNumber": 0 - } - } - ] - } - } -} diff --git a/spine/testdata/wallbox_detaileddiscoverydata_recv_notify.json b/integration_tests/testdata/wallbox_detaileddiscoverydata_recv_notify.json similarity index 100% rename from spine/testdata/wallbox_detaileddiscoverydata_recv_notify.json rename to integration_tests/testdata/wallbox_detaileddiscoverydata_recv_notify.json diff --git a/spine/testdata/wallbox_detaileddiscoverydata_recv_reply.json b/integration_tests/testdata/wallbox_detaileddiscoverydata_recv_reply.json similarity index 100% rename from spine/testdata/wallbox_detaileddiscoverydata_recv_reply.json rename to integration_tests/testdata/wallbox_detaileddiscoverydata_recv_reply.json diff --git a/logging/log.go b/logging/log.go deleted file mode 100644 index c3cf65c2..00000000 --- a/logging/log.go +++ /dev/null @@ -1,52 +0,0 @@ -package logging - -import "sync" - -//go:generate mockery --name=Logging - -// Logging needs to be implemented, if the internal logs should be printed -type Logging interface { - Trace(args ...interface{}) - Tracef(format string, args ...interface{}) - Debug(args ...interface{}) - Debugf(format string, args ...interface{}) - Info(args ...interface{}) - Infof(format string, args ...interface{}) - Error(args ...interface{}) - Errorf(format string, args ...interface{}) -} - -// NoLogging is an empty implementation of Logging which does nothing. -type NoLogging struct{} - -func (l *NoLogging) Trace(args ...interface{}) {} -func (l *NoLogging) Tracef(format string, args ...interface{}) {} -func (l *NoLogging) Debug(args ...interface{}) {} -func (l *NoLogging) Debugf(format string, args ...interface{}) {} -func (l *NoLogging) Info(args ...interface{}) {} -func (l *NoLogging) Infof(format string, args ...interface{}) {} -func (l *NoLogging) Error(args ...interface{}) {} -func (l *NoLogging) Errorf(format string, args ...interface{}) {} - -var log Logging = &NoLogging{} -var mux sync.Mutex - -// Sets a custom logging implementation -// By default NoLogging is used, so no logs are printed -// This is used by service.SetLogging() -func SetLogging(logger Logging) { - if logger == nil { - return - } - mux.Lock() - defer mux.Unlock() - - log = logger -} - -func Log() Logging { - mux.Lock() - defer mux.Unlock() - - return log -} diff --git a/logging/mocks/Logging.go b/logging/mocks/Logging.go deleted file mode 100644 index d88ff663..00000000 --- a/logging/mocks/Logging.go +++ /dev/null @@ -1,84 +0,0 @@ -// Code generated by mockery v2.39.1. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// Logging is an autogenerated mock type for the Logging type -type Logging struct { - mock.Mock -} - -// Debug provides a mock function with given fields: args -func (_m *Logging) Debug(args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// Debugf provides a mock function with given fields: format, args -func (_m *Logging) Debugf(format string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, format) - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// Error provides a mock function with given fields: args -func (_m *Logging) Error(args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// Errorf provides a mock function with given fields: format, args -func (_m *Logging) Errorf(format string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, format) - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// Info provides a mock function with given fields: args -func (_m *Logging) Info(args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// Infof provides a mock function with given fields: format, args -func (_m *Logging) Infof(format string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, format) - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// Trace provides a mock function with given fields: args -func (_m *Logging) Trace(args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// Tracef provides a mock function with given fields: format, args -func (_m *Logging) Tracef(format string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, format) - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// NewLogging creates a new instance of Logging. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewLogging(t interface { - mock.TestingT - Cleanup(func()) -}) *Logging { - mock := &Logging{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/mdns/.mockery.yaml b/mdns/.mockery.yaml new file mode 100644 index 00000000..e6c55316 --- /dev/null +++ b/mdns/.mockery.yaml @@ -0,0 +1,9 @@ +with-expecter: True +inpackage: false +dir: ../mocks/{{ replaceAll .InterfaceDirRelative "internal" "internal_" }} +mockname: "{{.InterfaceName}}" +outpkg: "mocks" +filename: "{{.InterfaceName}}.go" +all: True +packages: + github.com/enbility/eebus-go/mdns: diff --git a/service/mdns/api.go b/mdns/api.go similarity index 88% rename from service/mdns/api.go rename to mdns/api.go index f7c383fb..f514900f 100644 --- a/service/mdns/api.go +++ b/mdns/api.go @@ -2,7 +2,7 @@ package mdns import "net" -//go:generate mockery --name=MdnsProvider +//go:generate mockery type MdnsProvider interface { CheckAvailability() bool diff --git a/service/mdns/avahi.go b/mdns/avahi.go similarity index 99% rename from service/mdns/avahi.go rename to mdns/avahi.go index 4cba47f4..8ca50112 100644 --- a/service/mdns/avahi.go +++ b/mdns/avahi.go @@ -3,7 +3,7 @@ package mdns import ( "net" - "github.com/enbility/eebus-go/logging" + "github.com/enbility/ship-go/logging" "github.com/godbus/dbus/v5" "github.com/holoplot/go-avahi" ) diff --git a/service/mdns/helper.go b/mdns/helper.go similarity index 100% rename from service/mdns/helper.go rename to mdns/helper.go diff --git a/service/mdns/types.go b/mdns/types.go similarity index 100% rename from service/mdns/types.go rename to mdns/types.go diff --git a/service/mdns/zeroconf.go b/mdns/zeroconf.go similarity index 98% rename from service/mdns/zeroconf.go rename to mdns/zeroconf.go index 987762c2..3a077255 100644 --- a/service/mdns/zeroconf.go +++ b/mdns/zeroconf.go @@ -5,7 +5,7 @@ import ( "net" "github.com/DerAndereAndi/zeroconf/v2" - "github.com/enbility/eebus-go/logging" + "github.com/enbility/ship-go/logging" ) type ZeroconfProvider struct { diff --git a/mocks/ConnectionsHub.go b/mocks/ConnectionsHub.go new file mode 100644 index 00000000..b65cbf9c --- /dev/null +++ b/mocks/ConnectionsHub.go @@ -0,0 +1,393 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/eebus-go/api" + mock "github.com/stretchr/testify/mock" +) + +// ConnectionsHub is an autogenerated mock type for the ConnectionsHub type +type ConnectionsHub struct { + mock.Mock +} + +type ConnectionsHub_Expecter struct { + mock *mock.Mock +} + +func (_m *ConnectionsHub) EXPECT() *ConnectionsHub_Expecter { + return &ConnectionsHub_Expecter{mock: &_m.Mock} +} + +// CancelPairingWithSKI provides a mock function with given fields: ski +func (_m *ConnectionsHub) CancelPairingWithSKI(ski string) { + _m.Called(ski) +} + +// ConnectionsHub_CancelPairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelPairingWithSKI' +type ConnectionsHub_CancelPairingWithSKI_Call struct { + *mock.Call +} + +// CancelPairingWithSKI is a helper method to define mock.On call +// - ski string +func (_e *ConnectionsHub_Expecter) CancelPairingWithSKI(ski interface{}) *ConnectionsHub_CancelPairingWithSKI_Call { + return &ConnectionsHub_CancelPairingWithSKI_Call{Call: _e.mock.On("CancelPairingWithSKI", ski)} +} + +func (_c *ConnectionsHub_CancelPairingWithSKI_Call) Run(run func(ski string)) *ConnectionsHub_CancelPairingWithSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ConnectionsHub_CancelPairingWithSKI_Call) Return() *ConnectionsHub_CancelPairingWithSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectionsHub_CancelPairingWithSKI_Call) RunAndReturn(run func(string)) *ConnectionsHub_CancelPairingWithSKI_Call { + _c.Call.Return(run) + return _c +} + +// DisconnectSKI provides a mock function with given fields: ski, reason +func (_m *ConnectionsHub) DisconnectSKI(ski string, reason string) { + _m.Called(ski, reason) +} + +// ConnectionsHub_DisconnectSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectSKI' +type ConnectionsHub_DisconnectSKI_Call struct { + *mock.Call +} + +// DisconnectSKI is a helper method to define mock.On call +// - ski string +// - reason string +func (_e *ConnectionsHub_Expecter) DisconnectSKI(ski interface{}, reason interface{}) *ConnectionsHub_DisconnectSKI_Call { + return &ConnectionsHub_DisconnectSKI_Call{Call: _e.mock.On("DisconnectSKI", ski, reason)} +} + +func (_c *ConnectionsHub_DisconnectSKI_Call) Run(run func(ski string, reason string)) *ConnectionsHub_DisconnectSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *ConnectionsHub_DisconnectSKI_Call) Return() *ConnectionsHub_DisconnectSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectionsHub_DisconnectSKI_Call) RunAndReturn(run func(string, string)) *ConnectionsHub_DisconnectSKI_Call { + _c.Call.Return(run) + return _c +} + +// InitiatePairingWithSKI provides a mock function with given fields: ski +func (_m *ConnectionsHub) InitiatePairingWithSKI(ski string) { + _m.Called(ski) +} + +// ConnectionsHub_InitiatePairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitiatePairingWithSKI' +type ConnectionsHub_InitiatePairingWithSKI_Call struct { + *mock.Call +} + +// InitiatePairingWithSKI is a helper method to define mock.On call +// - ski string +func (_e *ConnectionsHub_Expecter) InitiatePairingWithSKI(ski interface{}) *ConnectionsHub_InitiatePairingWithSKI_Call { + return &ConnectionsHub_InitiatePairingWithSKI_Call{Call: _e.mock.On("InitiatePairingWithSKI", ski)} +} + +func (_c *ConnectionsHub_InitiatePairingWithSKI_Call) Run(run func(ski string)) *ConnectionsHub_InitiatePairingWithSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ConnectionsHub_InitiatePairingWithSKI_Call) Return() *ConnectionsHub_InitiatePairingWithSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectionsHub_InitiatePairingWithSKI_Call) RunAndReturn(run func(string)) *ConnectionsHub_InitiatePairingWithSKI_Call { + _c.Call.Return(run) + return _c +} + +// PairingDetailForSki provides a mock function with given fields: ski +func (_m *ConnectionsHub) PairingDetailForSki(ski string) *api.ConnectionStateDetail { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for PairingDetailForSki") + } + + var r0 *api.ConnectionStateDetail + if rf, ok := ret.Get(0).(func(string) *api.ConnectionStateDetail); ok { + r0 = rf(ski) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*api.ConnectionStateDetail) + } + } + + return r0 +} + +// ConnectionsHub_PairingDetailForSki_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PairingDetailForSki' +type ConnectionsHub_PairingDetailForSki_Call struct { + *mock.Call +} + +// PairingDetailForSki is a helper method to define mock.On call +// - ski string +func (_e *ConnectionsHub_Expecter) PairingDetailForSki(ski interface{}) *ConnectionsHub_PairingDetailForSki_Call { + return &ConnectionsHub_PairingDetailForSki_Call{Call: _e.mock.On("PairingDetailForSki", ski)} +} + +func (_c *ConnectionsHub_PairingDetailForSki_Call) Run(run func(ski string)) *ConnectionsHub_PairingDetailForSki_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ConnectionsHub_PairingDetailForSki_Call) Return(_a0 *api.ConnectionStateDetail) *ConnectionsHub_PairingDetailForSki_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectionsHub_PairingDetailForSki_Call) RunAndReturn(run func(string) *api.ConnectionStateDetail) *ConnectionsHub_PairingDetailForSki_Call { + _c.Call.Return(run) + return _c +} + +// RegisterRemoteSKI provides a mock function with given fields: ski, enable +func (_m *ConnectionsHub) RegisterRemoteSKI(ski string, enable bool) { + _m.Called(ski, enable) +} + +// ConnectionsHub_RegisterRemoteSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterRemoteSKI' +type ConnectionsHub_RegisterRemoteSKI_Call struct { + *mock.Call +} + +// RegisterRemoteSKI is a helper method to define mock.On call +// - ski string +// - enable bool +func (_e *ConnectionsHub_Expecter) RegisterRemoteSKI(ski interface{}, enable interface{}) *ConnectionsHub_RegisterRemoteSKI_Call { + return &ConnectionsHub_RegisterRemoteSKI_Call{Call: _e.mock.On("RegisterRemoteSKI", ski, enable)} +} + +func (_c *ConnectionsHub_RegisterRemoteSKI_Call) Run(run func(ski string, enable bool)) *ConnectionsHub_RegisterRemoteSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(bool)) + }) + return _c +} + +func (_c *ConnectionsHub_RegisterRemoteSKI_Call) Return() *ConnectionsHub_RegisterRemoteSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectionsHub_RegisterRemoteSKI_Call) RunAndReturn(run func(string, bool)) *ConnectionsHub_RegisterRemoteSKI_Call { + _c.Call.Return(run) + return _c +} + +// ServiceForSKI provides a mock function with given fields: ski +func (_m *ConnectionsHub) ServiceForSKI(ski string) *api.ServiceDetails { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for ServiceForSKI") + } + + var r0 *api.ServiceDetails + if rf, ok := ret.Get(0).(func(string) *api.ServiceDetails); ok { + r0 = rf(ski) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*api.ServiceDetails) + } + } + + return r0 +} + +// ConnectionsHub_ServiceForSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServiceForSKI' +type ConnectionsHub_ServiceForSKI_Call struct { + *mock.Call +} + +// ServiceForSKI is a helper method to define mock.On call +// - ski string +func (_e *ConnectionsHub_Expecter) ServiceForSKI(ski interface{}) *ConnectionsHub_ServiceForSKI_Call { + return &ConnectionsHub_ServiceForSKI_Call{Call: _e.mock.On("ServiceForSKI", ski)} +} + +func (_c *ConnectionsHub_ServiceForSKI_Call) Run(run func(ski string)) *ConnectionsHub_ServiceForSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ConnectionsHub_ServiceForSKI_Call) Return(_a0 *api.ServiceDetails) *ConnectionsHub_ServiceForSKI_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectionsHub_ServiceForSKI_Call) RunAndReturn(run func(string) *api.ServiceDetails) *ConnectionsHub_ServiceForSKI_Call { + _c.Call.Return(run) + return _c +} + +// Shutdown provides a mock function with given fields: +func (_m *ConnectionsHub) Shutdown() { + _m.Called() +} + +// ConnectionsHub_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown' +type ConnectionsHub_Shutdown_Call struct { + *mock.Call +} + +// Shutdown is a helper method to define mock.On call +func (_e *ConnectionsHub_Expecter) Shutdown() *ConnectionsHub_Shutdown_Call { + return &ConnectionsHub_Shutdown_Call{Call: _e.mock.On("Shutdown")} +} + +func (_c *ConnectionsHub_Shutdown_Call) Run(run func()) *ConnectionsHub_Shutdown_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectionsHub_Shutdown_Call) Return() *ConnectionsHub_Shutdown_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectionsHub_Shutdown_Call) RunAndReturn(run func()) *ConnectionsHub_Shutdown_Call { + _c.Call.Return(run) + return _c +} + +// Start provides a mock function with given fields: +func (_m *ConnectionsHub) Start() { + _m.Called() +} + +// ConnectionsHub_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' +type ConnectionsHub_Start_Call struct { + *mock.Call +} + +// Start is a helper method to define mock.On call +func (_e *ConnectionsHub_Expecter) Start() *ConnectionsHub_Start_Call { + return &ConnectionsHub_Start_Call{Call: _e.mock.On("Start")} +} + +func (_c *ConnectionsHub_Start_Call) Run(run func()) *ConnectionsHub_Start_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectionsHub_Start_Call) Return() *ConnectionsHub_Start_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectionsHub_Start_Call) RunAndReturn(run func()) *ConnectionsHub_Start_Call { + _c.Call.Return(run) + return _c +} + +// StartBrowseMdnsSearch provides a mock function with given fields: +func (_m *ConnectionsHub) StartBrowseMdnsSearch() { + _m.Called() +} + +// ConnectionsHub_StartBrowseMdnsSearch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartBrowseMdnsSearch' +type ConnectionsHub_StartBrowseMdnsSearch_Call struct { + *mock.Call +} + +// StartBrowseMdnsSearch is a helper method to define mock.On call +func (_e *ConnectionsHub_Expecter) StartBrowseMdnsSearch() *ConnectionsHub_StartBrowseMdnsSearch_Call { + return &ConnectionsHub_StartBrowseMdnsSearch_Call{Call: _e.mock.On("StartBrowseMdnsSearch")} +} + +func (_c *ConnectionsHub_StartBrowseMdnsSearch_Call) Run(run func()) *ConnectionsHub_StartBrowseMdnsSearch_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectionsHub_StartBrowseMdnsSearch_Call) Return() *ConnectionsHub_StartBrowseMdnsSearch_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectionsHub_StartBrowseMdnsSearch_Call) RunAndReturn(run func()) *ConnectionsHub_StartBrowseMdnsSearch_Call { + _c.Call.Return(run) + return _c +} + +// StopBrowseMdnsSearch provides a mock function with given fields: +func (_m *ConnectionsHub) StopBrowseMdnsSearch() { + _m.Called() +} + +// ConnectionsHub_StopBrowseMdnsSearch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StopBrowseMdnsSearch' +type ConnectionsHub_StopBrowseMdnsSearch_Call struct { + *mock.Call +} + +// StopBrowseMdnsSearch is a helper method to define mock.On call +func (_e *ConnectionsHub_Expecter) StopBrowseMdnsSearch() *ConnectionsHub_StopBrowseMdnsSearch_Call { + return &ConnectionsHub_StopBrowseMdnsSearch_Call{Call: _e.mock.On("StopBrowseMdnsSearch")} +} + +func (_c *ConnectionsHub_StopBrowseMdnsSearch_Call) Run(run func()) *ConnectionsHub_StopBrowseMdnsSearch_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectionsHub_StopBrowseMdnsSearch_Call) Return() *ConnectionsHub_StopBrowseMdnsSearch_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectionsHub_StopBrowseMdnsSearch_Call) RunAndReturn(run func()) *ConnectionsHub_StopBrowseMdnsSearch_Call { + _c.Call.Return(run) + return _c +} + +// NewConnectionsHub creates a new instance of ConnectionsHub. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewConnectionsHub(t interface { + mock.TestingT + Cleanup(func()) +}) *ConnectionsHub { + mock := &ConnectionsHub{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/EEBUSService.go b/mocks/EEBUSService.go new file mode 100644 index 00000000..436a2584 --- /dev/null +++ b/mocks/EEBUSService.go @@ -0,0 +1,522 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/eebus-go/api" + logging "github.com/enbility/ship-go/logging" + + mock "github.com/stretchr/testify/mock" + + spine_goapi "github.com/enbility/spine-go/api" +) + +// EEBUSService is an autogenerated mock type for the EEBUSService type +type EEBUSService struct { + mock.Mock +} + +type EEBUSService_Expecter struct { + mock *mock.Mock +} + +func (_m *EEBUSService) EXPECT() *EEBUSService_Expecter { + return &EEBUSService_Expecter{mock: &_m.Mock} +} + +// CancelPairingWithSKI provides a mock function with given fields: ski +func (_m *EEBUSService) CancelPairingWithSKI(ski string) { + _m.Called(ski) +} + +// EEBUSService_CancelPairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelPairingWithSKI' +type EEBUSService_CancelPairingWithSKI_Call struct { + *mock.Call +} + +// CancelPairingWithSKI is a helper method to define mock.On call +// - ski string +func (_e *EEBUSService_Expecter) CancelPairingWithSKI(ski interface{}) *EEBUSService_CancelPairingWithSKI_Call { + return &EEBUSService_CancelPairingWithSKI_Call{Call: _e.mock.On("CancelPairingWithSKI", ski)} +} + +func (_c *EEBUSService_CancelPairingWithSKI_Call) Run(run func(ski string)) *EEBUSService_CancelPairingWithSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *EEBUSService_CancelPairingWithSKI_Call) Return() *EEBUSService_CancelPairingWithSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_CancelPairingWithSKI_Call) RunAndReturn(run func(string)) *EEBUSService_CancelPairingWithSKI_Call { + _c.Call.Return(run) + return _c +} + +// DisconnectSKI provides a mock function with given fields: ski, reason +func (_m *EEBUSService) DisconnectSKI(ski string, reason string) { + _m.Called(ski, reason) +} + +// EEBUSService_DisconnectSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectSKI' +type EEBUSService_DisconnectSKI_Call struct { + *mock.Call +} + +// DisconnectSKI is a helper method to define mock.On call +// - ski string +// - reason string +func (_e *EEBUSService_Expecter) DisconnectSKI(ski interface{}, reason interface{}) *EEBUSService_DisconnectSKI_Call { + return &EEBUSService_DisconnectSKI_Call{Call: _e.mock.On("DisconnectSKI", ski, reason)} +} + +func (_c *EEBUSService_DisconnectSKI_Call) Run(run func(ski string, reason string)) *EEBUSService_DisconnectSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *EEBUSService_DisconnectSKI_Call) Return() *EEBUSService_DisconnectSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_DisconnectSKI_Call) RunAndReturn(run func(string, string)) *EEBUSService_DisconnectSKI_Call { + _c.Call.Return(run) + return _c +} + +// InitiatePairingWithSKI provides a mock function with given fields: ski +func (_m *EEBUSService) InitiatePairingWithSKI(ski string) { + _m.Called(ski) +} + +// EEBUSService_InitiatePairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitiatePairingWithSKI' +type EEBUSService_InitiatePairingWithSKI_Call struct { + *mock.Call +} + +// InitiatePairingWithSKI is a helper method to define mock.On call +// - ski string +func (_e *EEBUSService_Expecter) InitiatePairingWithSKI(ski interface{}) *EEBUSService_InitiatePairingWithSKI_Call { + return &EEBUSService_InitiatePairingWithSKI_Call{Call: _e.mock.On("InitiatePairingWithSKI", ski)} +} + +func (_c *EEBUSService_InitiatePairingWithSKI_Call) Run(run func(ski string)) *EEBUSService_InitiatePairingWithSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *EEBUSService_InitiatePairingWithSKI_Call) Return() *EEBUSService_InitiatePairingWithSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_InitiatePairingWithSKI_Call) RunAndReturn(run func(string)) *EEBUSService_InitiatePairingWithSKI_Call { + _c.Call.Return(run) + return _c +} + +// LocalDevice provides a mock function with given fields: +func (_m *EEBUSService) LocalDevice() spine_goapi.DeviceLocal { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LocalDevice") + } + + var r0 spine_goapi.DeviceLocal + if rf, ok := ret.Get(0).(func() spine_goapi.DeviceLocal); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(spine_goapi.DeviceLocal) + } + } + + return r0 +} + +// EEBUSService_LocalDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalDevice' +type EEBUSService_LocalDevice_Call struct { + *mock.Call +} + +// LocalDevice is a helper method to define mock.On call +func (_e *EEBUSService_Expecter) LocalDevice() *EEBUSService_LocalDevice_Call { + return &EEBUSService_LocalDevice_Call{Call: _e.mock.On("LocalDevice")} +} + +func (_c *EEBUSService_LocalDevice_Call) Run(run func()) *EEBUSService_LocalDevice_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EEBUSService_LocalDevice_Call) Return(_a0 spine_goapi.DeviceLocal) *EEBUSService_LocalDevice_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EEBUSService_LocalDevice_Call) RunAndReturn(run func() spine_goapi.DeviceLocal) *EEBUSService_LocalDevice_Call { + _c.Call.Return(run) + return _c +} + +// PairingDetailForSki provides a mock function with given fields: ski +func (_m *EEBUSService) PairingDetailForSki(ski string) *api.ConnectionStateDetail { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for PairingDetailForSki") + } + + var r0 *api.ConnectionStateDetail + if rf, ok := ret.Get(0).(func(string) *api.ConnectionStateDetail); ok { + r0 = rf(ski) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*api.ConnectionStateDetail) + } + } + + return r0 +} + +// EEBUSService_PairingDetailForSki_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PairingDetailForSki' +type EEBUSService_PairingDetailForSki_Call struct { + *mock.Call +} + +// PairingDetailForSki is a helper method to define mock.On call +// - ski string +func (_e *EEBUSService_Expecter) PairingDetailForSki(ski interface{}) *EEBUSService_PairingDetailForSki_Call { + return &EEBUSService_PairingDetailForSki_Call{Call: _e.mock.On("PairingDetailForSki", ski)} +} + +func (_c *EEBUSService_PairingDetailForSki_Call) Run(run func(ski string)) *EEBUSService_PairingDetailForSki_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *EEBUSService_PairingDetailForSki_Call) Return(_a0 *api.ConnectionStateDetail) *EEBUSService_PairingDetailForSki_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EEBUSService_PairingDetailForSki_Call) RunAndReturn(run func(string) *api.ConnectionStateDetail) *EEBUSService_PairingDetailForSki_Call { + _c.Call.Return(run) + return _c +} + +// RegisterRemoteSKI provides a mock function with given fields: ski, enable +func (_m *EEBUSService) RegisterRemoteSKI(ski string, enable bool) { + _m.Called(ski, enable) +} + +// EEBUSService_RegisterRemoteSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterRemoteSKI' +type EEBUSService_RegisterRemoteSKI_Call struct { + *mock.Call +} + +// RegisterRemoteSKI is a helper method to define mock.On call +// - ski string +// - enable bool +func (_e *EEBUSService_Expecter) RegisterRemoteSKI(ski interface{}, enable interface{}) *EEBUSService_RegisterRemoteSKI_Call { + return &EEBUSService_RegisterRemoteSKI_Call{Call: _e.mock.On("RegisterRemoteSKI", ski, enable)} +} + +func (_c *EEBUSService_RegisterRemoteSKI_Call) Run(run func(ski string, enable bool)) *EEBUSService_RegisterRemoteSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(bool)) + }) + return _c +} + +func (_c *EEBUSService_RegisterRemoteSKI_Call) Return() *EEBUSService_RegisterRemoteSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_RegisterRemoteSKI_Call) RunAndReturn(run func(string, bool)) *EEBUSService_RegisterRemoteSKI_Call { + _c.Call.Return(run) + return _c +} + +// RemoteServiceForSKI provides a mock function with given fields: ski +func (_m *EEBUSService) RemoteServiceForSKI(ski string) *api.ServiceDetails { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for RemoteServiceForSKI") + } + + var r0 *api.ServiceDetails + if rf, ok := ret.Get(0).(func(string) *api.ServiceDetails); ok { + r0 = rf(ski) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*api.ServiceDetails) + } + } + + return r0 +} + +// EEBUSService_RemoteServiceForSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteServiceForSKI' +type EEBUSService_RemoteServiceForSKI_Call struct { + *mock.Call +} + +// RemoteServiceForSKI is a helper method to define mock.On call +// - ski string +func (_e *EEBUSService_Expecter) RemoteServiceForSKI(ski interface{}) *EEBUSService_RemoteServiceForSKI_Call { + return &EEBUSService_RemoteServiceForSKI_Call{Call: _e.mock.On("RemoteServiceForSKI", ski)} +} + +func (_c *EEBUSService_RemoteServiceForSKI_Call) Run(run func(ski string)) *EEBUSService_RemoteServiceForSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *EEBUSService_RemoteServiceForSKI_Call) Return(_a0 *api.ServiceDetails) *EEBUSService_RemoteServiceForSKI_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EEBUSService_RemoteServiceForSKI_Call) RunAndReturn(run func(string) *api.ServiceDetails) *EEBUSService_RemoteServiceForSKI_Call { + _c.Call.Return(run) + return _c +} + +// SetLogging provides a mock function with given fields: logger +func (_m *EEBUSService) SetLogging(logger logging.Logging) { + _m.Called(logger) +} + +// EEBUSService_SetLogging_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetLogging' +type EEBUSService_SetLogging_Call struct { + *mock.Call +} + +// SetLogging is a helper method to define mock.On call +// - logger logging.Logging +func (_e *EEBUSService_Expecter) SetLogging(logger interface{}) *EEBUSService_SetLogging_Call { + return &EEBUSService_SetLogging_Call{Call: _e.mock.On("SetLogging", logger)} +} + +func (_c *EEBUSService_SetLogging_Call) Run(run func(logger logging.Logging)) *EEBUSService_SetLogging_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(logging.Logging)) + }) + return _c +} + +func (_c *EEBUSService_SetLogging_Call) Return() *EEBUSService_SetLogging_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_SetLogging_Call) RunAndReturn(run func(logging.Logging)) *EEBUSService_SetLogging_Call { + _c.Call.Return(run) + return _c +} + +// Setup provides a mock function with given fields: +func (_m *EEBUSService) Setup() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Setup") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EEBUSService_Setup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Setup' +type EEBUSService_Setup_Call struct { + *mock.Call +} + +// Setup is a helper method to define mock.On call +func (_e *EEBUSService_Expecter) Setup() *EEBUSService_Setup_Call { + return &EEBUSService_Setup_Call{Call: _e.mock.On("Setup")} +} + +func (_c *EEBUSService_Setup_Call) Run(run func()) *EEBUSService_Setup_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EEBUSService_Setup_Call) Return(_a0 error) *EEBUSService_Setup_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EEBUSService_Setup_Call) RunAndReturn(run func() error) *EEBUSService_Setup_Call { + _c.Call.Return(run) + return _c +} + +// Shutdown provides a mock function with given fields: +func (_m *EEBUSService) Shutdown() { + _m.Called() +} + +// EEBUSService_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown' +type EEBUSService_Shutdown_Call struct { + *mock.Call +} + +// Shutdown is a helper method to define mock.On call +func (_e *EEBUSService_Expecter) Shutdown() *EEBUSService_Shutdown_Call { + return &EEBUSService_Shutdown_Call{Call: _e.mock.On("Shutdown")} +} + +func (_c *EEBUSService_Shutdown_Call) Run(run func()) *EEBUSService_Shutdown_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EEBUSService_Shutdown_Call) Return() *EEBUSService_Shutdown_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_Shutdown_Call) RunAndReturn(run func()) *EEBUSService_Shutdown_Call { + _c.Call.Return(run) + return _c +} + +// Start provides a mock function with given fields: +func (_m *EEBUSService) Start() { + _m.Called() +} + +// EEBUSService_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' +type EEBUSService_Start_Call struct { + *mock.Call +} + +// Start is a helper method to define mock.On call +func (_e *EEBUSService_Expecter) Start() *EEBUSService_Start_Call { + return &EEBUSService_Start_Call{Call: _e.mock.On("Start")} +} + +func (_c *EEBUSService_Start_Call) Run(run func()) *EEBUSService_Start_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EEBUSService_Start_Call) Return() *EEBUSService_Start_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_Start_Call) RunAndReturn(run func()) *EEBUSService_Start_Call { + _c.Call.Return(run) + return _c +} + +// StartBrowseMdnsEntries provides a mock function with given fields: +func (_m *EEBUSService) StartBrowseMdnsEntries() { + _m.Called() +} + +// EEBUSService_StartBrowseMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartBrowseMdnsEntries' +type EEBUSService_StartBrowseMdnsEntries_Call struct { + *mock.Call +} + +// StartBrowseMdnsEntries is a helper method to define mock.On call +func (_e *EEBUSService_Expecter) StartBrowseMdnsEntries() *EEBUSService_StartBrowseMdnsEntries_Call { + return &EEBUSService_StartBrowseMdnsEntries_Call{Call: _e.mock.On("StartBrowseMdnsEntries")} +} + +func (_c *EEBUSService_StartBrowseMdnsEntries_Call) Run(run func()) *EEBUSService_StartBrowseMdnsEntries_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EEBUSService_StartBrowseMdnsEntries_Call) Return() *EEBUSService_StartBrowseMdnsEntries_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_StartBrowseMdnsEntries_Call) RunAndReturn(run func()) *EEBUSService_StartBrowseMdnsEntries_Call { + _c.Call.Return(run) + return _c +} + +// StopBrowseMdnsEntries provides a mock function with given fields: +func (_m *EEBUSService) StopBrowseMdnsEntries() { + _m.Called() +} + +// EEBUSService_StopBrowseMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StopBrowseMdnsEntries' +type EEBUSService_StopBrowseMdnsEntries_Call struct { + *mock.Call +} + +// StopBrowseMdnsEntries is a helper method to define mock.On call +func (_e *EEBUSService_Expecter) StopBrowseMdnsEntries() *EEBUSService_StopBrowseMdnsEntries_Call { + return &EEBUSService_StopBrowseMdnsEntries_Call{Call: _e.mock.On("StopBrowseMdnsEntries")} +} + +func (_c *EEBUSService_StopBrowseMdnsEntries_Call) Run(run func()) *EEBUSService_StopBrowseMdnsEntries_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EEBUSService_StopBrowseMdnsEntries_Call) Return() *EEBUSService_StopBrowseMdnsEntries_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSService_StopBrowseMdnsEntries_Call) RunAndReturn(run func()) *EEBUSService_StopBrowseMdnsEntries_Call { + _c.Call.Return(run) + return _c +} + +// NewEEBUSService creates a new instance of EEBUSService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEEBUSService(t interface { + mock.TestingT + Cleanup(func()) +}) *EEBUSService { + mock := &EEBUSService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/EEBUSServiceHandler.go b/mocks/EEBUSServiceHandler.go new file mode 100644 index 00000000..35b6f231 --- /dev/null +++ b/mocks/EEBUSServiceHandler.go @@ -0,0 +1,251 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/eebus-go/api" + mock "github.com/stretchr/testify/mock" +) + +// EEBUSServiceHandler is an autogenerated mock type for the EEBUSServiceHandler type +type EEBUSServiceHandler struct { + mock.Mock +} + +type EEBUSServiceHandler_Expecter struct { + mock *mock.Mock +} + +func (_m *EEBUSServiceHandler) EXPECT() *EEBUSServiceHandler_Expecter { + return &EEBUSServiceHandler_Expecter{mock: &_m.Mock} +} + +// AllowWaitingForTrust provides a mock function with given fields: ski +func (_m *EEBUSServiceHandler) AllowWaitingForTrust(ski string) bool { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for AllowWaitingForTrust") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(ski) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// EEBUSServiceHandler_AllowWaitingForTrust_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AllowWaitingForTrust' +type EEBUSServiceHandler_AllowWaitingForTrust_Call struct { + *mock.Call +} + +// AllowWaitingForTrust is a helper method to define mock.On call +// - ski string +func (_e *EEBUSServiceHandler_Expecter) AllowWaitingForTrust(ski interface{}) *EEBUSServiceHandler_AllowWaitingForTrust_Call { + return &EEBUSServiceHandler_AllowWaitingForTrust_Call{Call: _e.mock.On("AllowWaitingForTrust", ski)} +} + +func (_c *EEBUSServiceHandler_AllowWaitingForTrust_Call) Run(run func(ski string)) *EEBUSServiceHandler_AllowWaitingForTrust_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *EEBUSServiceHandler_AllowWaitingForTrust_Call) Return(_a0 bool) *EEBUSServiceHandler_AllowWaitingForTrust_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EEBUSServiceHandler_AllowWaitingForTrust_Call) RunAndReturn(run func(string) bool) *EEBUSServiceHandler_AllowWaitingForTrust_Call { + _c.Call.Return(run) + return _c +} + +// RemoteSKIConnected provides a mock function with given fields: service, ski +func (_m *EEBUSServiceHandler) RemoteSKIConnected(service api.EEBUSService, ski string) { + _m.Called(service, ski) +} + +// EEBUSServiceHandler_RemoteSKIConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIConnected' +type EEBUSServiceHandler_RemoteSKIConnected_Call struct { + *mock.Call +} + +// RemoteSKIConnected is a helper method to define mock.On call +// - service api.EEBUSService +// - ski string +func (_e *EEBUSServiceHandler_Expecter) RemoteSKIConnected(service interface{}, ski interface{}) *EEBUSServiceHandler_RemoteSKIConnected_Call { + return &EEBUSServiceHandler_RemoteSKIConnected_Call{Call: _e.mock.On("RemoteSKIConnected", service, ski)} +} + +func (_c *EEBUSServiceHandler_RemoteSKIConnected_Call) Run(run func(service api.EEBUSService, ski string)) *EEBUSServiceHandler_RemoteSKIConnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EEBUSService), args[1].(string)) + }) + return _c +} + +func (_c *EEBUSServiceHandler_RemoteSKIConnected_Call) Return() *EEBUSServiceHandler_RemoteSKIConnected_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSServiceHandler_RemoteSKIConnected_Call) RunAndReturn(run func(api.EEBUSService, string)) *EEBUSServiceHandler_RemoteSKIConnected_Call { + _c.Call.Return(run) + return _c +} + +// RemoteSKIDisconnected provides a mock function with given fields: service, ski +func (_m *EEBUSServiceHandler) RemoteSKIDisconnected(service api.EEBUSService, ski string) { + _m.Called(service, ski) +} + +// EEBUSServiceHandler_RemoteSKIDisconnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIDisconnected' +type EEBUSServiceHandler_RemoteSKIDisconnected_Call struct { + *mock.Call +} + +// RemoteSKIDisconnected is a helper method to define mock.On call +// - service api.EEBUSService +// - ski string +func (_e *EEBUSServiceHandler_Expecter) RemoteSKIDisconnected(service interface{}, ski interface{}) *EEBUSServiceHandler_RemoteSKIDisconnected_Call { + return &EEBUSServiceHandler_RemoteSKIDisconnected_Call{Call: _e.mock.On("RemoteSKIDisconnected", service, ski)} +} + +func (_c *EEBUSServiceHandler_RemoteSKIDisconnected_Call) Run(run func(service api.EEBUSService, ski string)) *EEBUSServiceHandler_RemoteSKIDisconnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EEBUSService), args[1].(string)) + }) + return _c +} + +func (_c *EEBUSServiceHandler_RemoteSKIDisconnected_Call) Return() *EEBUSServiceHandler_RemoteSKIDisconnected_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSServiceHandler_RemoteSKIDisconnected_Call) RunAndReturn(run func(api.EEBUSService, string)) *EEBUSServiceHandler_RemoteSKIDisconnected_Call { + _c.Call.Return(run) + return _c +} + +// ServicePairingDetailUpdate provides a mock function with given fields: ski, detail +func (_m *EEBUSServiceHandler) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { + _m.Called(ski, detail) +} + +// EEBUSServiceHandler_ServicePairingDetailUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServicePairingDetailUpdate' +type EEBUSServiceHandler_ServicePairingDetailUpdate_Call struct { + *mock.Call +} + +// ServicePairingDetailUpdate is a helper method to define mock.On call +// - ski string +// - detail *api.ConnectionStateDetail +func (_e *EEBUSServiceHandler_Expecter) ServicePairingDetailUpdate(ski interface{}, detail interface{}) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { + return &EEBUSServiceHandler_ServicePairingDetailUpdate_Call{Call: _e.mock.On("ServicePairingDetailUpdate", ski, detail)} +} + +func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) Run(run func(ski string, detail *api.ConnectionStateDetail)) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(*api.ConnectionStateDetail)) + }) + return _c +} + +func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) Return() *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) RunAndReturn(run func(string, *api.ConnectionStateDetail)) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { + _c.Call.Return(run) + return _c +} + +// ServiceShipIDUpdate provides a mock function with given fields: ski, shipdID +func (_m *EEBUSServiceHandler) ServiceShipIDUpdate(ski string, shipdID string) { + _m.Called(ski, shipdID) +} + +// EEBUSServiceHandler_ServiceShipIDUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServiceShipIDUpdate' +type EEBUSServiceHandler_ServiceShipIDUpdate_Call struct { + *mock.Call +} + +// ServiceShipIDUpdate is a helper method to define mock.On call +// - ski string +// - shipdID string +func (_e *EEBUSServiceHandler_Expecter) ServiceShipIDUpdate(ski interface{}, shipdID interface{}) *EEBUSServiceHandler_ServiceShipIDUpdate_Call { + return &EEBUSServiceHandler_ServiceShipIDUpdate_Call{Call: _e.mock.On("ServiceShipIDUpdate", ski, shipdID)} +} + +func (_c *EEBUSServiceHandler_ServiceShipIDUpdate_Call) Run(run func(ski string, shipdID string)) *EEBUSServiceHandler_ServiceShipIDUpdate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *EEBUSServiceHandler_ServiceShipIDUpdate_Call) Return() *EEBUSServiceHandler_ServiceShipIDUpdate_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSServiceHandler_ServiceShipIDUpdate_Call) RunAndReturn(run func(string, string)) *EEBUSServiceHandler_ServiceShipIDUpdate_Call { + _c.Call.Return(run) + return _c +} + +// VisibleRemoteServicesUpdated provides a mock function with given fields: service, entries +func (_m *EEBUSServiceHandler) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []api.RemoteService) { + _m.Called(service, entries) +} + +// EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VisibleRemoteServicesUpdated' +type EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call struct { + *mock.Call +} + +// VisibleRemoteServicesUpdated is a helper method to define mock.On call +// - service api.EEBUSService +// - entries []api.RemoteService +func (_e *EEBUSServiceHandler_Expecter) VisibleRemoteServicesUpdated(service interface{}, entries interface{}) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { + return &EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call{Call: _e.mock.On("VisibleRemoteServicesUpdated", service, entries)} +} + +func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) Run(run func(service api.EEBUSService, entries []api.RemoteService)) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EEBUSService), args[1].([]api.RemoteService)) + }) + return _c +} + +func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) Return() *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { + _c.Call.Return() + return _c +} + +func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) RunAndReturn(run func(api.EEBUSService, []api.RemoteService)) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { + _c.Call.Return(run) + return _c +} + +// NewEEBUSServiceHandler creates a new instance of EEBUSServiceHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEEBUSServiceHandler(t interface { + mock.TestingT + Cleanup(func()) +}) *EEBUSServiceHandler { + mock := &EEBUSServiceHandler{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/MdnsProvider.go b/mocks/MdnsProvider.go new file mode 100644 index 00000000..718b0b54 --- /dev/null +++ b/mocks/MdnsProvider.go @@ -0,0 +1,227 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + net "net" + + mock "github.com/stretchr/testify/mock" +) + +// MdnsProvider is an autogenerated mock type for the MdnsProvider type +type MdnsProvider struct { + mock.Mock +} + +type MdnsProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *MdnsProvider) EXPECT() *MdnsProvider_Expecter { + return &MdnsProvider_Expecter{mock: &_m.Mock} +} + +// Announce provides a mock function with given fields: serviceName, port, txt +func (_m *MdnsProvider) Announce(serviceName string, port int, txt []string) error { + ret := _m.Called(serviceName, port, txt) + + if len(ret) == 0 { + panic("no return value specified for Announce") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, int, []string) error); ok { + r0 = rf(serviceName, port, txt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MdnsProvider_Announce_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Announce' +type MdnsProvider_Announce_Call struct { + *mock.Call +} + +// Announce is a helper method to define mock.On call +// - serviceName string +// - port int +// - txt []string +func (_e *MdnsProvider_Expecter) Announce(serviceName interface{}, port interface{}, txt interface{}) *MdnsProvider_Announce_Call { + return &MdnsProvider_Announce_Call{Call: _e.mock.On("Announce", serviceName, port, txt)} +} + +func (_c *MdnsProvider_Announce_Call) Run(run func(serviceName string, port int, txt []string)) *MdnsProvider_Announce_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(int), args[2].([]string)) + }) + return _c +} + +func (_c *MdnsProvider_Announce_Call) Return(_a0 error) *MdnsProvider_Announce_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MdnsProvider_Announce_Call) RunAndReturn(run func(string, int, []string) error) *MdnsProvider_Announce_Call { + _c.Call.Return(run) + return _c +} + +// CheckAvailability provides a mock function with given fields: +func (_m *MdnsProvider) CheckAvailability() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for CheckAvailability") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// MdnsProvider_CheckAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckAvailability' +type MdnsProvider_CheckAvailability_Call struct { + *mock.Call +} + +// CheckAvailability is a helper method to define mock.On call +func (_e *MdnsProvider_Expecter) CheckAvailability() *MdnsProvider_CheckAvailability_Call { + return &MdnsProvider_CheckAvailability_Call{Call: _e.mock.On("CheckAvailability")} +} + +func (_c *MdnsProvider_CheckAvailability_Call) Run(run func()) *MdnsProvider_CheckAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MdnsProvider_CheckAvailability_Call) Return(_a0 bool) *MdnsProvider_CheckAvailability_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MdnsProvider_CheckAvailability_Call) RunAndReturn(run func() bool) *MdnsProvider_CheckAvailability_Call { + _c.Call.Return(run) + return _c +} + +// ResolveEntries provides a mock function with given fields: cancelChan, callback +func (_m *MdnsProvider) ResolveEntries(cancelChan chan bool, callback func(map[string]string, string, string, []net.IP, int, bool)) { + _m.Called(cancelChan, callback) +} + +// MdnsProvider_ResolveEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResolveEntries' +type MdnsProvider_ResolveEntries_Call struct { + *mock.Call +} + +// ResolveEntries is a helper method to define mock.On call +// - cancelChan chan bool +// - callback func(map[string]string , string , string , []net.IP , int , bool) +func (_e *MdnsProvider_Expecter) ResolveEntries(cancelChan interface{}, callback interface{}) *MdnsProvider_ResolveEntries_Call { + return &MdnsProvider_ResolveEntries_Call{Call: _e.mock.On("ResolveEntries", cancelChan, callback)} +} + +func (_c *MdnsProvider_ResolveEntries_Call) Run(run func(cancelChan chan bool, callback func(map[string]string, string, string, []net.IP, int, bool))) *MdnsProvider_ResolveEntries_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(chan bool), args[1].(func(map[string]string, string, string, []net.IP, int, bool))) + }) + return _c +} + +func (_c *MdnsProvider_ResolveEntries_Call) Return() *MdnsProvider_ResolveEntries_Call { + _c.Call.Return() + return _c +} + +func (_c *MdnsProvider_ResolveEntries_Call) RunAndReturn(run func(chan bool, func(map[string]string, string, string, []net.IP, int, bool))) *MdnsProvider_ResolveEntries_Call { + _c.Call.Return(run) + return _c +} + +// Shutdown provides a mock function with given fields: +func (_m *MdnsProvider) Shutdown() { + _m.Called() +} + +// MdnsProvider_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown' +type MdnsProvider_Shutdown_Call struct { + *mock.Call +} + +// Shutdown is a helper method to define mock.On call +func (_e *MdnsProvider_Expecter) Shutdown() *MdnsProvider_Shutdown_Call { + return &MdnsProvider_Shutdown_Call{Call: _e.mock.On("Shutdown")} +} + +func (_c *MdnsProvider_Shutdown_Call) Run(run func()) *MdnsProvider_Shutdown_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MdnsProvider_Shutdown_Call) Return() *MdnsProvider_Shutdown_Call { + _c.Call.Return() + return _c +} + +func (_c *MdnsProvider_Shutdown_Call) RunAndReturn(run func()) *MdnsProvider_Shutdown_Call { + _c.Call.Return(run) + return _c +} + +// Unannounce provides a mock function with given fields: +func (_m *MdnsProvider) Unannounce() { + _m.Called() +} + +// MdnsProvider_Unannounce_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Unannounce' +type MdnsProvider_Unannounce_Call struct { + *mock.Call +} + +// Unannounce is a helper method to define mock.On call +func (_e *MdnsProvider_Expecter) Unannounce() *MdnsProvider_Unannounce_Call { + return &MdnsProvider_Unannounce_Call{Call: _e.mock.On("Unannounce")} +} + +func (_c *MdnsProvider_Unannounce_Call) Run(run func()) *MdnsProvider_Unannounce_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MdnsProvider_Unannounce_Call) Return() *MdnsProvider_Unannounce_Call { + _c.Call.Return() + return _c +} + +func (_c *MdnsProvider_Unannounce_Call) RunAndReturn(run func()) *MdnsProvider_Unannounce_Call { + _c.Call.Return(run) + return _c +} + +// NewMdnsProvider creates a new instance of MdnsProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMdnsProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *MdnsProvider { + mock := &MdnsProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/MdnsSearch.go b/mocks/MdnsSearch.go new file mode 100644 index 00000000..ac749dcf --- /dev/null +++ b/mocks/MdnsSearch.go @@ -0,0 +1,68 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/eebus-go/api" + mock "github.com/stretchr/testify/mock" +) + +// MdnsSearch is an autogenerated mock type for the MdnsSearch type +type MdnsSearch struct { + mock.Mock +} + +type MdnsSearch_Expecter struct { + mock *mock.Mock +} + +func (_m *MdnsSearch) EXPECT() *MdnsSearch_Expecter { + return &MdnsSearch_Expecter{mock: &_m.Mock} +} + +// ReportMdnsEntries provides a mock function with given fields: entries +func (_m *MdnsSearch) ReportMdnsEntries(entries map[string]*api.MdnsEntry) { + _m.Called(entries) +} + +// MdnsSearch_ReportMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReportMdnsEntries' +type MdnsSearch_ReportMdnsEntries_Call struct { + *mock.Call +} + +// ReportMdnsEntries is a helper method to define mock.On call +// - entries map[string]*api.MdnsEntry +func (_e *MdnsSearch_Expecter) ReportMdnsEntries(entries interface{}) *MdnsSearch_ReportMdnsEntries_Call { + return &MdnsSearch_ReportMdnsEntries_Call{Call: _e.mock.On("ReportMdnsEntries", entries)} +} + +func (_c *MdnsSearch_ReportMdnsEntries_Call) Run(run func(entries map[string]*api.MdnsEntry)) *MdnsSearch_ReportMdnsEntries_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(map[string]*api.MdnsEntry)) + }) + return _c +} + +func (_c *MdnsSearch_ReportMdnsEntries_Call) Return() *MdnsSearch_ReportMdnsEntries_Call { + _c.Call.Return() + return _c +} + +func (_c *MdnsSearch_ReportMdnsEntries_Call) RunAndReturn(run func(map[string]*api.MdnsEntry)) *MdnsSearch_ReportMdnsEntries_Call { + _c.Call.Return(run) + return _c +} + +// NewMdnsSearch creates a new instance of MdnsSearch. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMdnsSearch(t interface { + mock.TestingT + Cleanup(func()) +}) *MdnsSearch { + mock := &MdnsSearch{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/MdnsService.go b/mocks/MdnsService.go new file mode 100644 index 00000000..c0dec29a --- /dev/null +++ b/mocks/MdnsService.go @@ -0,0 +1,255 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/eebus-go/api" + mock "github.com/stretchr/testify/mock" +) + +// MdnsService is an autogenerated mock type for the MdnsService type +type MdnsService struct { + mock.Mock +} + +type MdnsService_Expecter struct { + mock *mock.Mock +} + +func (_m *MdnsService) EXPECT() *MdnsService_Expecter { + return &MdnsService_Expecter{mock: &_m.Mock} +} + +// AnnounceMdnsEntry provides a mock function with given fields: +func (_m *MdnsService) AnnounceMdnsEntry() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for AnnounceMdnsEntry") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MdnsService_AnnounceMdnsEntry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AnnounceMdnsEntry' +type MdnsService_AnnounceMdnsEntry_Call struct { + *mock.Call +} + +// AnnounceMdnsEntry is a helper method to define mock.On call +func (_e *MdnsService_Expecter) AnnounceMdnsEntry() *MdnsService_AnnounceMdnsEntry_Call { + return &MdnsService_AnnounceMdnsEntry_Call{Call: _e.mock.On("AnnounceMdnsEntry")} +} + +func (_c *MdnsService_AnnounceMdnsEntry_Call) Run(run func()) *MdnsService_AnnounceMdnsEntry_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MdnsService_AnnounceMdnsEntry_Call) Return(_a0 error) *MdnsService_AnnounceMdnsEntry_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MdnsService_AnnounceMdnsEntry_Call) RunAndReturn(run func() error) *MdnsService_AnnounceMdnsEntry_Call { + _c.Call.Return(run) + return _c +} + +// RegisterMdnsSearch provides a mock function with given fields: cb +func (_m *MdnsService) RegisterMdnsSearch(cb api.MdnsSearch) { + _m.Called(cb) +} + +// MdnsService_RegisterMdnsSearch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterMdnsSearch' +type MdnsService_RegisterMdnsSearch_Call struct { + *mock.Call +} + +// RegisterMdnsSearch is a helper method to define mock.On call +// - cb api.MdnsSearch +func (_e *MdnsService_Expecter) RegisterMdnsSearch(cb interface{}) *MdnsService_RegisterMdnsSearch_Call { + return &MdnsService_RegisterMdnsSearch_Call{Call: _e.mock.On("RegisterMdnsSearch", cb)} +} + +func (_c *MdnsService_RegisterMdnsSearch_Call) Run(run func(cb api.MdnsSearch)) *MdnsService_RegisterMdnsSearch_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.MdnsSearch)) + }) + return _c +} + +func (_c *MdnsService_RegisterMdnsSearch_Call) Return() *MdnsService_RegisterMdnsSearch_Call { + _c.Call.Return() + return _c +} + +func (_c *MdnsService_RegisterMdnsSearch_Call) RunAndReturn(run func(api.MdnsSearch)) *MdnsService_RegisterMdnsSearch_Call { + _c.Call.Return(run) + return _c +} + +// SetupMdnsService provides a mock function with given fields: +func (_m *MdnsService) SetupMdnsService() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for SetupMdnsService") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MdnsService_SetupMdnsService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetupMdnsService' +type MdnsService_SetupMdnsService_Call struct { + *mock.Call +} + +// SetupMdnsService is a helper method to define mock.On call +func (_e *MdnsService_Expecter) SetupMdnsService() *MdnsService_SetupMdnsService_Call { + return &MdnsService_SetupMdnsService_Call{Call: _e.mock.On("SetupMdnsService")} +} + +func (_c *MdnsService_SetupMdnsService_Call) Run(run func()) *MdnsService_SetupMdnsService_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MdnsService_SetupMdnsService_Call) Return(_a0 error) *MdnsService_SetupMdnsService_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MdnsService_SetupMdnsService_Call) RunAndReturn(run func() error) *MdnsService_SetupMdnsService_Call { + _c.Call.Return(run) + return _c +} + +// ShutdownMdnsService provides a mock function with given fields: +func (_m *MdnsService) ShutdownMdnsService() { + _m.Called() +} + +// MdnsService_ShutdownMdnsService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ShutdownMdnsService' +type MdnsService_ShutdownMdnsService_Call struct { + *mock.Call +} + +// ShutdownMdnsService is a helper method to define mock.On call +func (_e *MdnsService_Expecter) ShutdownMdnsService() *MdnsService_ShutdownMdnsService_Call { + return &MdnsService_ShutdownMdnsService_Call{Call: _e.mock.On("ShutdownMdnsService")} +} + +func (_c *MdnsService_ShutdownMdnsService_Call) Run(run func()) *MdnsService_ShutdownMdnsService_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MdnsService_ShutdownMdnsService_Call) Return() *MdnsService_ShutdownMdnsService_Call { + _c.Call.Return() + return _c +} + +func (_c *MdnsService_ShutdownMdnsService_Call) RunAndReturn(run func()) *MdnsService_ShutdownMdnsService_Call { + _c.Call.Return(run) + return _c +} + +// UnannounceMdnsEntry provides a mock function with given fields: +func (_m *MdnsService) UnannounceMdnsEntry() { + _m.Called() +} + +// MdnsService_UnannounceMdnsEntry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnannounceMdnsEntry' +type MdnsService_UnannounceMdnsEntry_Call struct { + *mock.Call +} + +// UnannounceMdnsEntry is a helper method to define mock.On call +func (_e *MdnsService_Expecter) UnannounceMdnsEntry() *MdnsService_UnannounceMdnsEntry_Call { + return &MdnsService_UnannounceMdnsEntry_Call{Call: _e.mock.On("UnannounceMdnsEntry")} +} + +func (_c *MdnsService_UnannounceMdnsEntry_Call) Run(run func()) *MdnsService_UnannounceMdnsEntry_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MdnsService_UnannounceMdnsEntry_Call) Return() *MdnsService_UnannounceMdnsEntry_Call { + _c.Call.Return() + return _c +} + +func (_c *MdnsService_UnannounceMdnsEntry_Call) RunAndReturn(run func()) *MdnsService_UnannounceMdnsEntry_Call { + _c.Call.Return(run) + return _c +} + +// UnregisterMdnsSearch provides a mock function with given fields: cb +func (_m *MdnsService) UnregisterMdnsSearch(cb api.MdnsSearch) { + _m.Called(cb) +} + +// MdnsService_UnregisterMdnsSearch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnregisterMdnsSearch' +type MdnsService_UnregisterMdnsSearch_Call struct { + *mock.Call +} + +// UnregisterMdnsSearch is a helper method to define mock.On call +// - cb api.MdnsSearch +func (_e *MdnsService_Expecter) UnregisterMdnsSearch(cb interface{}) *MdnsService_UnregisterMdnsSearch_Call { + return &MdnsService_UnregisterMdnsSearch_Call{Call: _e.mock.On("UnregisterMdnsSearch", cb)} +} + +func (_c *MdnsService_UnregisterMdnsSearch_Call) Run(run func(cb api.MdnsSearch)) *MdnsService_UnregisterMdnsSearch_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.MdnsSearch)) + }) + return _c +} + +func (_c *MdnsService_UnregisterMdnsSearch_Call) Return() *MdnsService_UnregisterMdnsSearch_Call { + _c.Call.Return() + return _c +} + +func (_c *MdnsService_UnregisterMdnsSearch_Call) RunAndReturn(run func(api.MdnsSearch)) *MdnsService_UnregisterMdnsSearch_Call { + _c.Call.Return(run) + return _c +} + +// NewMdnsService creates a new instance of MdnsService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMdnsService(t interface { + mock.TestingT + Cleanup(func()) +}) *MdnsService { + mock := &MdnsService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/ServiceProvider.go b/mocks/ServiceProvider.go new file mode 100644 index 00000000..1e4c3888 --- /dev/null +++ b/mocks/ServiceProvider.go @@ -0,0 +1,248 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/eebus-go/api" + mock "github.com/stretchr/testify/mock" +) + +// ServiceProvider is an autogenerated mock type for the ServiceProvider type +type ServiceProvider struct { + mock.Mock +} + +type ServiceProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *ServiceProvider) EXPECT() *ServiceProvider_Expecter { + return &ServiceProvider_Expecter{mock: &_m.Mock} +} + +// AllowWaitingForTrust provides a mock function with given fields: ski +func (_m *ServiceProvider) AllowWaitingForTrust(ski string) bool { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for AllowWaitingForTrust") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(ski) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// ServiceProvider_AllowWaitingForTrust_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AllowWaitingForTrust' +type ServiceProvider_AllowWaitingForTrust_Call struct { + *mock.Call +} + +// AllowWaitingForTrust is a helper method to define mock.On call +// - ski string +func (_e *ServiceProvider_Expecter) AllowWaitingForTrust(ski interface{}) *ServiceProvider_AllowWaitingForTrust_Call { + return &ServiceProvider_AllowWaitingForTrust_Call{Call: _e.mock.On("AllowWaitingForTrust", ski)} +} + +func (_c *ServiceProvider_AllowWaitingForTrust_Call) Run(run func(ski string)) *ServiceProvider_AllowWaitingForTrust_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceProvider_AllowWaitingForTrust_Call) Return(_a0 bool) *ServiceProvider_AllowWaitingForTrust_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServiceProvider_AllowWaitingForTrust_Call) RunAndReturn(run func(string) bool) *ServiceProvider_AllowWaitingForTrust_Call { + _c.Call.Return(run) + return _c +} + +// RemoteSKIConnected provides a mock function with given fields: ski +func (_m *ServiceProvider) RemoteSKIConnected(ski string) { + _m.Called(ski) +} + +// ServiceProvider_RemoteSKIConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIConnected' +type ServiceProvider_RemoteSKIConnected_Call struct { + *mock.Call +} + +// RemoteSKIConnected is a helper method to define mock.On call +// - ski string +func (_e *ServiceProvider_Expecter) RemoteSKIConnected(ski interface{}) *ServiceProvider_RemoteSKIConnected_Call { + return &ServiceProvider_RemoteSKIConnected_Call{Call: _e.mock.On("RemoteSKIConnected", ski)} +} + +func (_c *ServiceProvider_RemoteSKIConnected_Call) Run(run func(ski string)) *ServiceProvider_RemoteSKIConnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceProvider_RemoteSKIConnected_Call) Return() *ServiceProvider_RemoteSKIConnected_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceProvider_RemoteSKIConnected_Call) RunAndReturn(run func(string)) *ServiceProvider_RemoteSKIConnected_Call { + _c.Call.Return(run) + return _c +} + +// RemoteSKIDisconnected provides a mock function with given fields: ski +func (_m *ServiceProvider) RemoteSKIDisconnected(ski string) { + _m.Called(ski) +} + +// ServiceProvider_RemoteSKIDisconnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIDisconnected' +type ServiceProvider_RemoteSKIDisconnected_Call struct { + *mock.Call +} + +// RemoteSKIDisconnected is a helper method to define mock.On call +// - ski string +func (_e *ServiceProvider_Expecter) RemoteSKIDisconnected(ski interface{}) *ServiceProvider_RemoteSKIDisconnected_Call { + return &ServiceProvider_RemoteSKIDisconnected_Call{Call: _e.mock.On("RemoteSKIDisconnected", ski)} +} + +func (_c *ServiceProvider_RemoteSKIDisconnected_Call) Run(run func(ski string)) *ServiceProvider_RemoteSKIDisconnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceProvider_RemoteSKIDisconnected_Call) Return() *ServiceProvider_RemoteSKIDisconnected_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceProvider_RemoteSKIDisconnected_Call) RunAndReturn(run func(string)) *ServiceProvider_RemoteSKIDisconnected_Call { + _c.Call.Return(run) + return _c +} + +// ServicePairingDetailUpdate provides a mock function with given fields: ski, detail +func (_m *ServiceProvider) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { + _m.Called(ski, detail) +} + +// ServiceProvider_ServicePairingDetailUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServicePairingDetailUpdate' +type ServiceProvider_ServicePairingDetailUpdate_Call struct { + *mock.Call +} + +// ServicePairingDetailUpdate is a helper method to define mock.On call +// - ski string +// - detail *api.ConnectionStateDetail +func (_e *ServiceProvider_Expecter) ServicePairingDetailUpdate(ski interface{}, detail interface{}) *ServiceProvider_ServicePairingDetailUpdate_Call { + return &ServiceProvider_ServicePairingDetailUpdate_Call{Call: _e.mock.On("ServicePairingDetailUpdate", ski, detail)} +} + +func (_c *ServiceProvider_ServicePairingDetailUpdate_Call) Run(run func(ski string, detail *api.ConnectionStateDetail)) *ServiceProvider_ServicePairingDetailUpdate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(*api.ConnectionStateDetail)) + }) + return _c +} + +func (_c *ServiceProvider_ServicePairingDetailUpdate_Call) Return() *ServiceProvider_ServicePairingDetailUpdate_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceProvider_ServicePairingDetailUpdate_Call) RunAndReturn(run func(string, *api.ConnectionStateDetail)) *ServiceProvider_ServicePairingDetailUpdate_Call { + _c.Call.Return(run) + return _c +} + +// ServiceShipIDUpdate provides a mock function with given fields: ski, shipID +func (_m *ServiceProvider) ServiceShipIDUpdate(ski string, shipID string) { + _m.Called(ski, shipID) +} + +// ServiceProvider_ServiceShipIDUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServiceShipIDUpdate' +type ServiceProvider_ServiceShipIDUpdate_Call struct { + *mock.Call +} + +// ServiceShipIDUpdate is a helper method to define mock.On call +// - ski string +// - shipID string +func (_e *ServiceProvider_Expecter) ServiceShipIDUpdate(ski interface{}, shipID interface{}) *ServiceProvider_ServiceShipIDUpdate_Call { + return &ServiceProvider_ServiceShipIDUpdate_Call{Call: _e.mock.On("ServiceShipIDUpdate", ski, shipID)} +} + +func (_c *ServiceProvider_ServiceShipIDUpdate_Call) Run(run func(ski string, shipID string)) *ServiceProvider_ServiceShipIDUpdate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *ServiceProvider_ServiceShipIDUpdate_Call) Return() *ServiceProvider_ServiceShipIDUpdate_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceProvider_ServiceShipIDUpdate_Call) RunAndReturn(run func(string, string)) *ServiceProvider_ServiceShipIDUpdate_Call { + _c.Call.Return(run) + return _c +} + +// VisibleMDNSRecordsUpdated provides a mock function with given fields: entries +func (_m *ServiceProvider) VisibleMDNSRecordsUpdated(entries []*api.MdnsEntry) { + _m.Called(entries) +} + +// ServiceProvider_VisibleMDNSRecordsUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VisibleMDNSRecordsUpdated' +type ServiceProvider_VisibleMDNSRecordsUpdated_Call struct { + *mock.Call +} + +// VisibleMDNSRecordsUpdated is a helper method to define mock.On call +// - entries []*api.MdnsEntry +func (_e *ServiceProvider_Expecter) VisibleMDNSRecordsUpdated(entries interface{}) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { + return &ServiceProvider_VisibleMDNSRecordsUpdated_Call{Call: _e.mock.On("VisibleMDNSRecordsUpdated", entries)} +} + +func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) Run(run func(entries []*api.MdnsEntry)) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]*api.MdnsEntry)) + }) + return _c +} + +func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) Return() *ServiceProvider_VisibleMDNSRecordsUpdated_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) RunAndReturn(run func([]*api.MdnsEntry)) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { + _c.Call.Return(run) + return _c +} + +// NewServiceProvider creates a new instance of ServiceProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServiceProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *ServiceProvider { + mock := &ServiceProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/mockgen_api.go b/mocks/mockgen_api.go new file mode 100644 index 00000000..fdfb3fc0 --- /dev/null +++ b/mocks/mockgen_api.go @@ -0,0 +1,213 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/enbility/eebus-go/api (interfaces: ServiceProvider,MdnsService) +// +// Generated by this command: +// +// mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider,MdnsService +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + api "github.com/enbility/eebus-go/api" + gomock "go.uber.org/mock/gomock" +) + +// MockServiceProvider is a mock of ServiceProvider interface. +type MockServiceProvider struct { + ctrl *gomock.Controller + recorder *MockServiceProviderMockRecorder +} + +// MockServiceProviderMockRecorder is the mock recorder for MockServiceProvider. +type MockServiceProviderMockRecorder struct { + mock *MockServiceProvider +} + +// NewMockServiceProvider creates a new mock instance. +func NewMockServiceProvider(ctrl *gomock.Controller) *MockServiceProvider { + mock := &MockServiceProvider{ctrl: ctrl} + mock.recorder = &MockServiceProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockServiceProvider) EXPECT() *MockServiceProviderMockRecorder { + return m.recorder +} + +// AllowWaitingForTrust mocks base method. +func (m *MockServiceProvider) AllowWaitingForTrust(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AllowWaitingForTrust", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. +func (mr *MockServiceProviderMockRecorder) AllowWaitingForTrust(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockServiceProvider)(nil).AllowWaitingForTrust), arg0) +} + +// RemoteSKIConnected mocks base method. +func (m *MockServiceProvider) RemoteSKIConnected(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoteSKIConnected", arg0) +} + +// RemoteSKIConnected indicates an expected call of RemoteSKIConnected. +func (mr *MockServiceProviderMockRecorder) RemoteSKIConnected(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIConnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIConnected), arg0) +} + +// RemoteSKIDisconnected mocks base method. +func (m *MockServiceProvider) RemoteSKIDisconnected(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoteSKIDisconnected", arg0) +} + +// RemoteSKIDisconnected indicates an expected call of RemoteSKIDisconnected. +func (mr *MockServiceProviderMockRecorder) RemoteSKIDisconnected(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIDisconnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIDisconnected), arg0) +} + +// ServicePairingDetailUpdate mocks base method. +func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 *api.ConnectionStateDetail) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ServicePairingDetailUpdate", arg0, arg1) +} + +// ServicePairingDetailUpdate indicates an expected call of ServicePairingDetailUpdate. +func (mr *MockServiceProviderMockRecorder) ServicePairingDetailUpdate(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServicePairingDetailUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServicePairingDetailUpdate), arg0, arg1) +} + +// ServiceShipIDUpdate mocks base method. +func (m *MockServiceProvider) ServiceShipIDUpdate(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ServiceShipIDUpdate", arg0, arg1) +} + +// ServiceShipIDUpdate indicates an expected call of ServiceShipIDUpdate. +func (mr *MockServiceProviderMockRecorder) ServiceShipIDUpdate(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceShipIDUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServiceShipIDUpdate), arg0, arg1) +} + +// VisibleMDNSRecordsUpdated mocks base method. +func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []*api.MdnsEntry) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "VisibleMDNSRecordsUpdated", arg0) +} + +// VisibleMDNSRecordsUpdated indicates an expected call of VisibleMDNSRecordsUpdated. +func (mr *MockServiceProviderMockRecorder) VisibleMDNSRecordsUpdated(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleMDNSRecordsUpdated", reflect.TypeOf((*MockServiceProvider)(nil).VisibleMDNSRecordsUpdated), arg0) +} + +// MockMdnsService is a mock of MdnsService interface. +type MockMdnsService struct { + ctrl *gomock.Controller + recorder *MockMdnsServiceMockRecorder +} + +// MockMdnsServiceMockRecorder is the mock recorder for MockMdnsService. +type MockMdnsServiceMockRecorder struct { + mock *MockMdnsService +} + +// NewMockMdnsService creates a new mock instance. +func NewMockMdnsService(ctrl *gomock.Controller) *MockMdnsService { + mock := &MockMdnsService{ctrl: ctrl} + mock.recorder = &MockMdnsServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMdnsService) EXPECT() *MockMdnsServiceMockRecorder { + return m.recorder +} + +// AnnounceMdnsEntry mocks base method. +func (m *MockMdnsService) AnnounceMdnsEntry() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AnnounceMdnsEntry") + ret0, _ := ret[0].(error) + return ret0 +} + +// AnnounceMdnsEntry indicates an expected call of AnnounceMdnsEntry. +func (mr *MockMdnsServiceMockRecorder) AnnounceMdnsEntry() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnnounceMdnsEntry", reflect.TypeOf((*MockMdnsService)(nil).AnnounceMdnsEntry)) +} + +// RegisterMdnsSearch mocks base method. +func (m *MockMdnsService) RegisterMdnsSearch(arg0 api.MdnsSearch) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterMdnsSearch", arg0) +} + +// RegisterMdnsSearch indicates an expected call of RegisterMdnsSearch. +func (mr *MockMdnsServiceMockRecorder) RegisterMdnsSearch(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).RegisterMdnsSearch), arg0) +} + +// SetupMdnsService mocks base method. +func (m *MockMdnsService) SetupMdnsService() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetupMdnsService") + ret0, _ := ret[0].(error) + return ret0 +} + +// SetupMdnsService indicates an expected call of SetupMdnsService. +func (mr *MockMdnsServiceMockRecorder) SetupMdnsService() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupMdnsService", reflect.TypeOf((*MockMdnsService)(nil).SetupMdnsService)) +} + +// ShutdownMdnsService mocks base method. +func (m *MockMdnsService) ShutdownMdnsService() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ShutdownMdnsService") +} + +// ShutdownMdnsService indicates an expected call of ShutdownMdnsService. +func (mr *MockMdnsServiceMockRecorder) ShutdownMdnsService() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShutdownMdnsService", reflect.TypeOf((*MockMdnsService)(nil).ShutdownMdnsService)) +} + +// UnannounceMdnsEntry mocks base method. +func (m *MockMdnsService) UnannounceMdnsEntry() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UnannounceMdnsEntry") +} + +// UnannounceMdnsEntry indicates an expected call of UnannounceMdnsEntry. +func (mr *MockMdnsServiceMockRecorder) UnannounceMdnsEntry() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnannounceMdnsEntry", reflect.TypeOf((*MockMdnsService)(nil).UnannounceMdnsEntry)) +} + +// UnregisterMdnsSearch mocks base method. +func (m *MockMdnsService) UnregisterMdnsSearch(arg0 api.MdnsSearch) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UnregisterMdnsSearch", arg0) +} + +// UnregisterMdnsSearch indicates an expected call of UnregisterMdnsSearch. +func (mr *MockMdnsServiceMockRecorder) UnregisterMdnsSearch(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).UnregisterMdnsSearch), arg0) +} diff --git a/service/hub.go b/service/hub.go index 48e55615..410cdaeb 100644 --- a/service/hub.go +++ b/service/hub.go @@ -15,10 +15,15 @@ import ( "sync" "time" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/logging" + shipmodel "github.com/enbility/ship-go/model" + "github.com/enbility/ship-go/ship" + shipws "github.com/enbility/ship-go/ws" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/gorilla/websocket" ) @@ -42,30 +47,30 @@ var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ // handling all connections to remote services type connectionsHubImpl struct { - connections map[string]ship.ShipConnection + connections map[string]shipapi.ShipConnection // which attempt is it to initate an connection to the remote SKI connectionAttemptCounter map[string]int connectionAttemptRunning map[string]bool - configuration *Configuration - localService *ServiceDetails + configuration *api.Configuration + localService *api.ServiceDetails - serviceProvider ServiceProvider + serviceProvider api.ServiceProvider // The list of known remote services - remoteServices map[string]*ServiceDetails + remoteServices map[string]*api.ServiceDetails // The web server for handling incoming websocket connections httpServer *http.Server // Handling mDNS related tasks - mdns MdnsService + mdns api.MdnsService // list of currently known/reported mDNS entries - knownMdnsEntries []*MdnsEntry + knownMdnsEntries []*api.MdnsEntry - spineLocalDevice spine.DeviceLocal + spineLocalDevice spineapi.DeviceLocal muxCon sync.Mutex muxConAttempt sync.Mutex @@ -73,13 +78,13 @@ type connectionsHubImpl struct { muxMdns sync.Mutex } -func newConnectionsHub(serviceProvider ServiceProvider, mdns MdnsService, spineLocalDevice spine.DeviceLocal, configuration *Configuration, localService *ServiceDetails) ConnectionsHub { +func newConnectionsHub(serviceProvider api.ServiceProvider, mdns api.MdnsService, spineLocalDevice spineapi.DeviceLocal, configuration *api.Configuration, localService *api.ServiceDetails) api.ConnectionsHub { hub := &connectionsHubImpl{ - connections: make(map[string]ship.ShipConnection), + connections: make(map[string]shipapi.ShipConnection), connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), - remoteServices: make(map[string]*ServiceDetails), - knownMdnsEntries: make([]*MdnsEntry, 0), + remoteServices: make(map[string]*api.ServiceDetails), + knownMdnsEntries: make([]*api.MdnsEntry, 0), serviceProvider: serviceProvider, spineLocalDevice: spineLocalDevice, configuration: configuration, @@ -106,7 +111,7 @@ func (h *connectionsHubImpl) Start() { h.checkRestartMdnsSearch() } -var _ ship.ShipServiceDataProvider = (*connectionsHubImpl)(nil) +var _ shipapi.ShipServiceDataProvider = (*connectionsHubImpl)(nil) // Returns if the provided SKI is from a registered service func (h *connectionsHubImpl) IsRemoteServiceForSKIPaired(ski string) bool { @@ -116,7 +121,7 @@ func (h *connectionsHubImpl) IsRemoteServiceForSKIPaired(ski string) bool { } // The connection was closed, we need to clean up -func (h *connectionsHubImpl) HandleConnectionClosed(connection ship.ShipConnection, handshakeCompleted bool) { +func (h *connectionsHubImpl) HandleConnectionClosed(connection shipapi.ShipConnection, handshakeCompleted bool) { remoteSki := connection.RemoteSKI() if h.spineLocalDevice != nil { h.spineLocalDevice.RemoveRemoteDeviceConnection(remoteSki) @@ -148,7 +153,7 @@ func (h *connectionsHubImpl) HandleConnectionClosed(connection ship.ShipConnecti h.checkRestartMdnsSearch() } -func (h *connectionsHubImpl) SetupRemoteDevice(ski string, writeI ship.SpineDataConnection) ship.SpineDataProcessing { +func (h *connectionsHubImpl) SetupRemoteDevice(ski string, writeI shipapi.SpineDataConnection) shipapi.SpineDataProcessing { return h.spineLocalDevice.SetupRemoteDevice(ski, writeI) } @@ -213,23 +218,24 @@ func (h *connectionsHubImpl) AllowWaitingForTrust(ski string) bool { } // Provides the current ship message exchange state for a given SKI and the corresponding error if state is error -func (h *connectionsHubImpl) HandleShipHandshakeStateUpdate(ski string, state ship.ShipState) { +func (h *connectionsHubImpl) HandleShipHandshakeStateUpdate(ski string, state shipmodel.ShipState) { // overwrite service Paired value - if state.State == ship.SmeHelloStateOk { + if state.State == shipmodel.SmeHelloStateOk { h.RegisterRemoteSKI(ski, true) } pairingState := h.mapShipMessageExchangeState(state.State, ski) - if state.Error != nil && state.Error != ErrConnectionNotFound { - pairingState = ConnectionStateError + if state.Error != nil && state.Error != api.ErrConnectionNotFound { + pairingState = api.ConnectionStateError } - pairingDetail := NewConnectionStateDetail(pairingState, state.Error) + pairingDetail := api.NewConnectionStateDetail(pairingState, state.Error) service := h.ServiceForSKI(ski) existingDetails := service.ConnectionStateDetail() - if existingDetails.State() != pairingState || existingDetails.Error() != state.Error { + existingState := existingDetails.State() + if existingState != pairingState || existingDetails.Error() != state.Error { service.SetConnectionStateDetail(pairingDetail) h.serviceProvider.ServicePairingDetailUpdate(ski, pairingDetail) @@ -242,52 +248,52 @@ func (h *connectionsHubImpl) HandleShipHandshakeStateUpdate(ski string, state sh // // ErrNotPaired if the SKI is not in the (to be) paired list // ErrNoConnectionFound if no connection for the SKI was found -func (h *connectionsHubImpl) PairingDetailForSki(ski string) *ConnectionStateDetail { +func (h *connectionsHubImpl) PairingDetailForSki(ski string) *api.ConnectionStateDetail { service := h.ServiceForSKI(ski) if conn := h.connectionForSKI(ski); conn != nil { shipState, shipError := conn.ShipHandshakeState() state := h.mapShipMessageExchangeState(shipState, ski) - return NewConnectionStateDetail(state, shipError) + return api.NewConnectionStateDetail(state, shipError) } return service.ConnectionStateDetail() } // maps ShipMessageExchangeState to PairingState -func (h *connectionsHubImpl) mapShipMessageExchangeState(state ship.ShipMessageExchangeState, ski string) ConnectionState { - var connState ConnectionState +func (h *connectionsHubImpl) mapShipMessageExchangeState(state shipmodel.ShipMessageExchangeState, ski string) api.ConnectionState { + var connState api.ConnectionState // map the SHIP states to a public gState switch state { - case ship.CmiStateInitStart: - connState = ConnectionStateQueued - case ship.CmiStateClientSend, ship.CmiStateClientWait, ship.CmiStateClientEvaluate, - ship.CmiStateServerWait, ship.CmiStateServerEvaluate: - connState = ConnectionStateInitiated - case ship.SmeHelloStateReadyInit, ship.SmeHelloStateReadyListen, ship.SmeHelloStateReadyTimeout: - connState = ConnectionStateInProgress - case ship.SmeHelloStatePendingInit, ship.SmeHelloStatePendingListen, ship.SmeHelloStatePendingTimeout: - connState = ConnectionStateReceivedPairingRequest - case ship.SmeHelloStateOk: - connState = ConnectionStateTrusted - case ship.SmeHelloStateAbort, ship.SmeHelloStateAbortDone: - connState = ConnectionStateNone - case ship.SmeHelloStateRemoteAbortDone, ship.SmeHelloStateRejected: - connState = ConnectionStateRemoteDeniedTrust - case ship.SmePinStateCheckInit, ship.SmePinStateCheckListen, ship.SmePinStateCheckError, - ship.SmePinStateCheckBusyInit, ship.SmePinStateCheckBusyWait, ship.SmePinStateCheckOk, - ship.SmePinStateAskInit, ship.SmePinStateAskProcess, ship.SmePinStateAskRestricted, - ship.SmePinStateAskOk: - connState = ConnectionStatePin - case ship.SmeAccessMethodsRequest, ship.SmeStateApproved: - connState = ConnectionStateInProgress - case ship.SmeStateComplete: - connState = ConnectionStateCompleted - case ship.SmeStateError: - connState = ConnectionStateError + case shipmodel.CmiStateInitStart: + connState = api.ConnectionStateQueued + case shipmodel.CmiStateClientSend, shipmodel.CmiStateClientWait, shipmodel.CmiStateClientEvaluate, + shipmodel.CmiStateServerWait, shipmodel.CmiStateServerEvaluate: + connState = api.ConnectionStateInitiated + case shipmodel.SmeHelloStateReadyInit, shipmodel.SmeHelloStateReadyListen, shipmodel.SmeHelloStateReadyTimeout: + connState = api.ConnectionStateInProgress + case shipmodel.SmeHelloStatePendingInit, shipmodel.SmeHelloStatePendingListen, shipmodel.SmeHelloStatePendingTimeout: + connState = api.ConnectionStateReceivedPairingRequest + case shipmodel.SmeHelloStateOk: + connState = api.ConnectionStateTrusted + case shipmodel.SmeHelloStateAbort, shipmodel.SmeHelloStateAbortDone: + connState = api.ConnectionStateNone + case shipmodel.SmeHelloStateRemoteAbortDone, shipmodel.SmeHelloStateRejected: + connState = api.ConnectionStateRemoteDeniedTrust + case shipmodel.SmePinStateCheckInit, shipmodel.SmePinStateCheckListen, shipmodel.SmePinStateCheckError, + shipmodel.SmePinStateCheckBusyInit, shipmodel.SmePinStateCheckBusyWait, shipmodel.SmePinStateCheckOk, + shipmodel.SmePinStateAskInit, shipmodel.SmePinStateAskProcess, shipmodel.SmePinStateAskRestricted, + shipmodel.SmePinStateAskOk: + connState = api.ConnectionStatePin + case shipmodel.SmeAccessMethodsRequest, shipmodel.SmeStateApproved: + connState = api.ConnectionStateInProgress + case shipmodel.SmeStateComplete: + connState = api.ConnectionStateCompleted + case shipmodel.SmeStateError: + connState = api.ConnectionStateError default: - connState = ConnectionStateInProgress + connState = api.ConnectionStateInProgress } return connState @@ -309,14 +315,14 @@ func (h *connectionsHubImpl) DisconnectSKI(ski string, reason string) { } // register a new ship Connection -func (h *connectionsHubImpl) registerConnection(connection ship.ShipConnection) { +func (h *connectionsHubImpl) registerConnection(connection shipapi.ShipConnection) { h.muxCon.Lock() h.connections[connection.RemoteSKI()] = connection h.muxCon.Unlock() } // return the connection for a specific SKI -func (h *connectionsHubImpl) connectionForSKI(ski string) ship.ShipConnection { +func (h *connectionsHubImpl) connectionForSKI(ski string) shipapi.ShipConnection { h.muxCon.Lock() defer h.muxCon.Unlock() @@ -349,12 +355,12 @@ func (h *connectionsHubImpl) isSkiConnected(ski string) bool { func (h *connectionsHubImpl) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { skiFound := false for _, v := range rawCerts { - cert, err := x509.ParseCertificate(v) + cerificate, err := x509.ParseCertificate(v) if err != nil { return err } - if _, err := skiFromCertificate(cert); err == nil { + if _, err := cert.SkiFromCertificate(cerificate); err == nil { skiFound = true break } @@ -368,16 +374,16 @@ func (h *connectionsHubImpl) verifyPeerCertificate(rawCerts [][]byte, verifiedCh // start the ship websocket server func (h *connectionsHubImpl) startWebsocketServer() error { - addr := fmt.Sprintf(":%d", h.configuration.port) + addr := fmt.Sprintf(":%d", h.configuration.Port()) logging.Log().Debug("starting websocket server on", addr) h.httpServer = &http.Server{ Addr: addr, Handler: h, TLSConfig: &tls.Config{ - Certificates: []tls.Certificate{h.configuration.certificate}, + Certificates: []tls.Certificate{h.configuration.Certificate()}, ClientAuth: tls.RequireAnyClientCert, // SHIP 9: Client authentication is required - CipherSuites: ciperSuites, + CipherSuites: cert.CiperSuites, VerifyPeerCertificate: h.verifyPeerCertificate, }, } @@ -397,8 +403,8 @@ func (h *connectionsHubImpl) startWebsocketServer() error { // HTTP Server callback for handling incoming connection requests func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { upgrader := websocket.Upgrader{ - ReadBufferSize: ship.MaxMessageSize, - WriteBufferSize: ship.MaxMessageSize, + ReadBufferSize: shipws.MaxMessageSize, + WriteBufferSize: shipws.MaxMessageSize, CheckOrigin: func(r *http.Request) bool { return true }, Subprotocols: []string{shipWebsocketSubProtocol}, // SHIP 10.2: Sub protocol "ship" is required } @@ -423,7 +429,7 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - ski, err := skiFromCertificate(r.TLS.PeerCertificates[0]) + ski, err := cert.SkiFromCertificate(r.TLS.PeerCertificates[0]) if err != nil { logging.Log().Debug(err) _ = conn.Close() @@ -431,14 +437,14 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // normalize the incoming SKI - remoteService := NewServiceDetails(ski) + remoteService := api.NewServiceDetails(ski) logging.Log().Debug("incoming connection request from", remoteService.SKI) // Check if the remote service is paired service := h.ServiceForSKI(remoteService.SKI) connectionStateDetail := service.ConnectionStateDetail() - if connectionStateDetail.State() == ConnectionStateQueued { - connectionStateDetail.SetState(ConnectionStateReceivedPairingRequest) + if connectionStateDetail.State() == api.ConnectionStateQueued { + connectionStateDetail.SetState(api.ConnectionStateReceivedPairingRequest) h.serviceProvider.ServicePairingDetailUpdate(ski, connectionStateDetail) } @@ -450,7 +456,7 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI) + dataHandler := shipws.NewWebsocketConnection(conn, remoteService.SKI) shipConnection := ship.NewConnectionHandler(h, dataHandler, ship.ShipRoleServer, h.localService.ShipID, remoteService.SKI, remoteService.ShipID) shipConnection.Run() @@ -461,7 +467,7 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Connect to another EEBUS service // // returns error contains a reason for failing the connection or nil if no further tries should be processed -func (h *connectionsHubImpl) connectFoundService(remoteService *ServiceDetails, host, port string) error { +func (h *connectionsHubImpl) connectFoundService(remoteService *api.ServiceDetails, host, port string) error { if h.isSkiConnected(remoteService.SKI) { return nil } @@ -472,9 +478,9 @@ func (h *connectionsHubImpl) connectFoundService(remoteService *ServiceDetails, Proxy: http.ProxyFromEnvironment, HandshakeTimeout: 5 * time.Second, TLSClientConfig: &tls.Config{ - Certificates: []tls.Certificate{h.configuration.certificate}, + Certificates: []tls.Certificate{h.configuration.Certificate()}, InsecureSkipVerify: true, - CipherSuites: ciperSuites, + CipherSuites: cert.CiperSuites, }, Subprotocols: []string{shipWebsocketSubProtocol}, } @@ -495,7 +501,7 @@ func (h *connectionsHubImpl) connectFoundService(remoteService *ServiceDetails, return errors.New(errorString) } - if _, err := skiFromCertificate(remoteCerts[0]); err != nil { + if _, err := cert.SkiFromCertificate(remoteCerts[0]); err != nil { // Close connection as the remote SKI can't be correct errorString := fmt.Sprintf("closing connection to %s: %s", remoteService.SKI, err) conn.Close() @@ -515,7 +521,7 @@ func (h *connectionsHubImpl) connectFoundService(remoteService *ServiceDetails, return errors.New(errorString) } - dataHandler := ship.NewWebsocketConnection(conn, remoteService.SKI) + dataHandler := shipws.NewWebsocketConnection(conn, remoteService.SKI) shipConnection := ship.NewConnectionHandler(h, dataHandler, ship.ShipRoleClient, h.localService.ShipID, remoteService.SKI, remoteService.ShipID) shipConnection.Run() @@ -530,7 +536,7 @@ func (h *connectionsHubImpl) connectFoundService(remoteService *ServiceDetails, // // returns true if this connection is fine to be continue // returns false if this connection should not be established or kept -func (h *connectionsHubImpl) keepThisConnection(conn *websocket.Conn, incomingRequest bool, remoteService *ServiceDetails) bool { +func (h *connectionsHubImpl) keepThisConnection(conn *websocket.Conn, incomingRequest bool, remoteService *api.ServiceDetails) bool { // SHIP 12.2.2 defines: // prevent double connections with SKI Comparison // the node with the hight SKI value kees the most recent connection and @@ -576,14 +582,14 @@ func (h *connectionsHubImpl) keepThisConnection(conn *websocket.Conn, incomingRe } // return the service for a given SKI or an error if not found -func (h *connectionsHubImpl) ServiceForSKI(ski string) *ServiceDetails { +func (h *connectionsHubImpl) ServiceForSKI(ski string) *api.ServiceDetails { h.muxReg.Lock() defer h.muxReg.Unlock() service, ok := h.remoteServices[ski] if !ok { - service = NewServiceDetails(ski) - service.ConnectionStateDetail().SetState(ConnectionStateNone) + service = api.NewServiceDetails(ski) + service.ConnectionStateDetail().SetState(api.ConnectionStateNone) h.remoteServices[ski] = service } @@ -604,7 +610,7 @@ func (h *connectionsHubImpl) RegisterRemoteSKI(ski string, enable bool) { h.removeConnectionAttemptCounter(ski) - service.ConnectionStateDetail().SetState(ConnectionStateNone) + service.ConnectionStateDetail().SetState(api.ConnectionStateNone) h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) @@ -626,7 +632,7 @@ func (h *connectionsHubImpl) InitiatePairingWithSKI(ski string) { // locally initiated service := h.ServiceForSKI(ski) - service.ConnectionStateDetail().SetState(ConnectionStateQueued) + service.ConnectionStateDetail().SetState(api.ConnectionStateQueued) h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) @@ -645,18 +651,18 @@ func (h *connectionsHubImpl) CancelPairingWithSKI(ski string) { } service := h.ServiceForSKI(ski) - service.ConnectionStateDetail().SetState(ConnectionStateNone) + service.ConnectionStateDetail().SetState(api.ConnectionStateNone) service.Trusted = false h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) } // Process reported mDNS services -func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*MdnsEntry) { +func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*api.MdnsEntry) { h.muxMdns.Lock() defer h.muxMdns.Unlock() - var mdnsEntries []*MdnsEntry + var mdnsEntries []*api.MdnsEntry for ski, entry := range entries { mdnsEntries = append(mdnsEntries, entry) @@ -669,7 +675,7 @@ func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*MdnsEntry) { // Check if the remote service is paired or queued for connection service := h.ServiceForSKI(ski) if !h.IsRemoteServiceForSKIPaired(ski) && - service.ConnectionStateDetail().State() != ConnectionStateQueued { + service.ConnectionStateDetail().State() != api.ConnectionStateQueued { continue } @@ -697,7 +703,7 @@ func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*MdnsEntry) { } // coordinate connection initiation attempts to a remove service -func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *MdnsEntry) { +func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *api.MdnsEntry) { if h.isConnectionAttemptRunning(ski) { return } @@ -707,7 +713,7 @@ func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *M counter, duration := h.getConnectionInitiationDelayTime(ski) service := h.ServiceForSKI(ski) - if service.ConnectionStateDetail().State() == ConnectionStateQueued { + if service.ConnectionStateDetail().State() == api.ConnectionStateQueued { go h.prepareConnectionInitation(ski, counter, entry) return } @@ -726,7 +732,7 @@ func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *M // invoked by coordinateConnectionInitations either with a delay or directly // when initating a pairing process -func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, entry *MdnsEntry) { +func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, entry *api.MdnsEntry) { h.setConnectionAttemptRunning(ski, false) // check if the current counter is still the same, otherwise this counter is irrelevant @@ -738,7 +744,7 @@ func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing pairingState := h.ServiceForSKI(ski).ConnectionStateDetail().State() - if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != ConnectionStateQueued { + if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != api.ConnectionStateQueued { return } @@ -758,7 +764,7 @@ func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, // attempt to establish a connection to a remote service // returns true if successful -func (h *connectionsHubImpl) initateConnection(remoteService *ServiceDetails, entry *MdnsEntry) bool { +func (h *connectionsHubImpl) initateConnection(remoteService *api.ServiceDetails, entry *api.MdnsEntry) bool { var err error // try connecting via an IP address first @@ -766,7 +772,7 @@ func (h *connectionsHubImpl) initateConnection(remoteService *ServiceDetails, en // connection attempt is not relevant if the device is no longer paired // or it is not queued for pairing pairingState := h.ServiceForSKI(remoteService.SKI).ConnectionStateDetail().State() - if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != ConnectionStateQueued { + if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != api.ConnectionStateQueued { return false } diff --git a/service/hub_test.go b/service/hub_test.go index 7c05ff60..66d3355f 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -1,8 +1,15 @@ package service import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha1" "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" "errors" + "math/big" "net" "net/http" "net/http/httptest" @@ -11,9 +18,13 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/ship/mocks" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" + "github.com/enbility/eebus-go/mocks" + shipapi "github.com/enbility/ship-go/api" + shipmocks "github.com/enbility/ship-go/mocks" + shipmodel "github.com/enbility/ship-go/model" + "github.com/enbility/spine-go/model" "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -33,10 +44,13 @@ type testStruct struct { type HubSuite struct { suite.Suite - serviceProvider *MockServiceProvider - mdnsService *MockMdnsService - shipConnection *mocks.ShipConnection - shipDataConnection *mocks.WebsocketDataConnection + serviceProvider *mocks.MockServiceProvider + mdnsService *mocks.MockMdnsService + + // serviceProvider *mocks.ServiceProvider + // mdnsService *mocks.MdnsService + shipConnection *shipmocks.ShipConnection + wsDataConnection *shipmocks.WebsocketDataConnection remoteSki string @@ -45,7 +59,7 @@ type HubSuite struct { sut *connectionsHubImpl } -func (s *HubSuite) SetupSuite() { +func (s *HubSuite) BeforeTest(suiteName, testName string) { s.remoteSki = "remotetestski" s.tests = []testStruct{ @@ -63,59 +77,60 @@ func (s *HubSuite) SetupSuite() { } ctrl := gomock.NewController(s.T()) - - s.serviceProvider = NewMockServiceProvider(ctrl) - s.serviceProvider.EXPECT().RemoteSKIConnected(gomock.Any()).AnyTimes() - s.serviceProvider.EXPECT().ServiceShipIDUpdate(gomock.Any(), gomock.Any()).AnyTimes() - s.serviceProvider.EXPECT().ServicePairingDetailUpdate(gomock.Any(), gomock.Any()).AnyTimes() - s.serviceProvider.EXPECT().RemoteSKIDisconnected(gomock.Any()).AnyTimes() - s.serviceProvider.EXPECT().AllowWaitingForTrust(gomock.Any()).AnyTimes() - - s.mdnsService = NewMockMdnsService(ctrl) - s.mdnsService.EXPECT().SetupMdnsService().AnyTimes() - s.mdnsService.EXPECT().AnnounceMdnsEntry().AnyTimes() - s.mdnsService.EXPECT().UnannounceMdnsEntry().AnyTimes() - s.mdnsService.EXPECT().RegisterMdnsSearch(gomock.Any()).AnyTimes() - s.mdnsService.EXPECT().UnregisterMdnsSearch(gomock.Any()).AnyTimes() - - s.shipDataConnection = mocks.NewWebsocketDataConnection(s.T()) - - s.shipConnection = mocks.NewShipConnection(s.T()) - s.shipConnection.On("CloseConnection", mock.Anything, mock.Anything, mock.Anything).Return().Maybe() - s.shipConnection.On("RemoteSKI").Return(s.remoteSki).Maybe() - s.shipConnection.On("ApprovePendingHandshake").Return().Maybe() - s.shipConnection.On("AbortPendingHandshake").Return().Maybe() - s.shipConnection.On("DataHandler").Return(s.shipDataConnection).Maybe() - s.shipConnection.On("ShipHandshakeState").Return(ship.SmeStateComplete, nil).Maybe() -} - -func (s *HubSuite) BeforeTest(suiteName, testName string) { - localService := &ServiceDetails{ + // use gomock mocks instead of mockery, as those will panic with a data race error in these tests + + s.serviceProvider = mocks.NewMockServiceProvider(ctrl) + // s.serviceProvider = mocks.NewServiceProvider(s.T()) + s.serviceProvider.EXPECT().RemoteSKIConnected(gomock.Any()).Return().AnyTimes() + s.serviceProvider.EXPECT().ServiceShipIDUpdate(gomock.Any(), gomock.Any()).Return().AnyTimes() + s.serviceProvider.EXPECT().ServicePairingDetailUpdate(gomock.Any(), gomock.Any()).Return().AnyTimes() + s.serviceProvider.EXPECT().RemoteSKIDisconnected(gomock.Any()).Return().AnyTimes() + s.serviceProvider.EXPECT().AllowWaitingForTrust(gomock.Any()).Return(false).AnyTimes() + + s.mdnsService = mocks.NewMockMdnsService(ctrl) + // s.mdnsService = mocks.NewMdnsService(s.T()) + s.mdnsService.EXPECT().SetupMdnsService().Return(nil).AnyTimes() + s.mdnsService.EXPECT().AnnounceMdnsEntry().Return(nil).AnyTimes() + s.mdnsService.EXPECT().UnannounceMdnsEntry().Return().AnyTimes() + s.mdnsService.EXPECT().RegisterMdnsSearch(gomock.Any()).Return().AnyTimes() + s.mdnsService.EXPECT().UnregisterMdnsSearch(gomock.Any()).Return().AnyTimes() + + s.wsDataConnection = shipmocks.NewWebsocketDataConnection(s.T()) + + s.shipConnection = shipmocks.NewShipConnection(s.T()) + s.shipConnection.EXPECT().CloseConnection(mock.Anything, mock.Anything, mock.Anything).Return().Maybe() + s.shipConnection.EXPECT().RemoteSKI().Return(s.remoteSki).Maybe() + s.shipConnection.EXPECT().ApprovePendingHandshake().Return().Maybe() + s.shipConnection.EXPECT().AbortPendingHandshake().Return().Maybe() + s.shipConnection.EXPECT().DataHandler().Return(s.wsDataConnection).Maybe() + s.shipConnection.EXPECT().ShipHandshakeState().Return(shipmodel.SmeStateComplete, nil).Maybe() + + localService := &api.ServiceDetails{ SKI: "localSKI", } s.sut = &connectionsHubImpl{ - connections: make(map[string]ship.ShipConnection), + connections: make(map[string]shipapi.ShipConnection), connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), - remoteServices: make(map[string]*ServiceDetails), + remoteServices: make(map[string]*api.ServiceDetails), serviceProvider: s.serviceProvider, localService: localService, mdns: s.mdnsService, } - certificate, _ := CreateCertificate("unit", "org", "DE", "CN") - s.sut.configuration, _ = NewConfiguration("vendor", "brand", "model", "serial", + certificate, _ := cert.CreateCertificate("unit", "org", "DE", "CN") + s.sut.configuration, _ = api.NewConfiguration("vendor", "brand", "model", "serial", model.DeviceTypeTypeGeneric, []model.EntityTypeType{model.EntityTypeTypeCEM}, 4567, certificate, 230, time.Second*4) } func (s *HubSuite) Test_NewConnectionsHub() { ski := "12af9e" - localService := NewServiceDetails(ski) - configuration := &Configuration{ - interfaces: []string{"en0"}, - } + localService := api.NewServiceDetails(ski) + + configuration := &api.Configuration{} + configuration.SetInterfaces([]string{"en0"}) hub := newConnectionsHub(s.serviceProvider, s.mdnsService, nil, configuration, localService) assert.NotNil(s.T(), hub) @@ -168,13 +183,13 @@ func (s *HubSuite) Test_Mdns() { } func (s *HubSuite) Test_Ship() { - s.sut.HandleShipHandshakeStateUpdate(s.remoteSki, ship.ShipState{ - State: ship.SmeStateError, + s.sut.HandleShipHandshakeStateUpdate(s.remoteSki, shipmodel.ShipState{ + State: shipmodel.SmeStateError, Error: errors.New("test"), }) - s.sut.HandleShipHandshakeStateUpdate(s.remoteSki, ship.ShipState{ - State: ship.SmeHelloStateOk, + s.sut.HandleShipHandshakeStateUpdate(s.remoteSki, shipmodel.ShipState{ + State: shipmodel.SmeHelloStateOk, }) s.sut.ReportServiceShipID(s.remoteSki, "test") @@ -195,41 +210,41 @@ func (s *HubSuite) Test_Ship() { } func (s *HubSuite) Test_MapShipMessageExchangeState() { - state := s.sut.mapShipMessageExchangeState(ship.CmiStateInitStart, s.remoteSki) - assert.Equal(s.T(), ConnectionStateQueued, state) + state := s.sut.mapShipMessageExchangeState(shipmodel.CmiStateInitStart, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateQueued, state) - state = s.sut.mapShipMessageExchangeState(ship.CmiStateClientSend, s.remoteSki) - assert.Equal(s.T(), ConnectionStateInitiated, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.CmiStateClientSend, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateInitiated, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateReadyInit, s.remoteSki) - assert.Equal(s.T(), ConnectionStateInProgress, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStateReadyInit, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateInProgress, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStatePendingInit, s.remoteSki) - assert.Equal(s.T(), ConnectionStateReceivedPairingRequest, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStatePendingInit, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateReceivedPairingRequest, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateOk, s.remoteSki) - assert.Equal(s.T(), ConnectionStateTrusted, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStateOk, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateTrusted, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateAbort, s.remoteSki) - assert.Equal(s.T(), ConnectionStateNone, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStateAbort, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateNone, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeHelloStateRemoteAbortDone, s.remoteSki) - assert.Equal(s.T(), ConnectionStateRemoteDeniedTrust, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStateRemoteAbortDone, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateRemoteDeniedTrust, state) - state = s.sut.mapShipMessageExchangeState(ship.SmePinStateCheckInit, s.remoteSki) - assert.Equal(s.T(), ConnectionStatePin, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmePinStateCheckInit, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStatePin, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeAccessMethodsRequest, s.remoteSki) - assert.Equal(s.T(), ConnectionStateInProgress, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeAccessMethodsRequest, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateInProgress, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeStateComplete, s.remoteSki) - assert.Equal(s.T(), ConnectionStateCompleted, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeStateComplete, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateCompleted, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeStateError, s.remoteSki) - assert.Equal(s.T(), ConnectionStateError, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeStateError, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateError, state) - state = s.sut.mapShipMessageExchangeState(ship.SmeProtHStateTimeout, s.remoteSki) - assert.Equal(s.T(), ConnectionStateInProgress, state) + state = s.sut.mapShipMessageExchangeState(shipmodel.SmeProtHStateTimeout, s.remoteSki) + assert.Equal(s.T(), api.ConnectionStateInProgress, state) } func (s *HubSuite) Test_DisconnectSKI() { @@ -249,7 +264,7 @@ func (s *HubSuite) Test_Shutdown() { } func (s *HubSuite) Test_VerifyPeerCertificate() { - testCert, _ := CreateCertificate("unit", "org", "DE", "CN") + testCert, _ := cert.CreateCertificate("unit", "org", "DE", "CN") var rawCerts [][]byte rawCerts = append(rawCerts, testCert.Certificate...) err := s.sut.verifyPeerCertificate(rawCerts, nil) @@ -261,14 +276,14 @@ func (s *HubSuite) Test_VerifyPeerCertificate() { assert.NotNil(s.T(), err) rawCerts = nil - invalidCert, _ := CreateInvalidCertificate("unit", "org", "DE", "CN") + invalidCert, _ := createInvalidCertificate("unit", "org", "DE", "CN") rawCerts = append(rawCerts, invalidCert.Certificate...) err = s.sut.verifyPeerCertificate(rawCerts, nil) assert.NotNil(s.T(), err) } -func (s *HubSuite) Test_ServeHTTP() { +func (s *HubSuite) Test_ServeHTTP_01() { req := httptest.NewRequest("GET", "http://example.com/foo", nil) w := httptest.NewRecorder() s.sut.ServeHTTP(w, req) @@ -286,43 +301,49 @@ func (s *HubSuite) Test_ServeHTTP() { } con, _, err = dialer.Dial(wsURL, nil) assert.Nil(s.T(), err) + con.Close() + server.CloseClientConnections() server.Close() - server = httptest.NewUnstartedServer(s.sut) + time.Sleep(time.Second) +} + +func (s *HubSuite) Test_ServeHTTP_02() { + server := httptest.NewUnstartedServer(s.sut) server.TLS = &tls.Config{ - Certificates: []tls.Certificate{s.sut.configuration.certificate}, + Certificates: []tls.Certificate{s.sut.configuration.Certificate()}, ClientAuth: tls.RequireAnyClientCert, - CipherSuites: ciperSuites, + CipherSuites: cert.CiperSuites, InsecureSkipVerify: true, } server.StartTLS() - wsURL = strings.Replace(server.URL, "https://", "wss://", -1) + wsURL := strings.Replace(server.URL, "https://", "wss://", -1) - invalidCert, _ := CreateInvalidCertificate("unit", "org", "DE", "CN") - dialer = &websocket.Dialer{ + invalidCert, _ := createInvalidCertificate("unit", "org", "DE", "CN") + dialer := &websocket.Dialer{ Proxy: http.ProxyFromEnvironment, HandshakeTimeout: 5 * time.Second, TLSClientConfig: &tls.Config{ Certificates: []tls.Certificate{invalidCert}, InsecureSkipVerify: true, - CipherSuites: ciperSuites, + CipherSuites: cert.CiperSuites, }, Subprotocols: []string{shipWebsocketSubProtocol}, } - con, _, err = dialer.Dial(wsURL, nil) + con, _, err := dialer.Dial(wsURL, nil) assert.Nil(s.T(), err) con.Close() - validCert, _ := CreateCertificate("unit", "org", "DE", "CN") + validCert, _ := cert.CreateCertificate("unit", "org", "DE", "CN") dialer = &websocket.Dialer{ Proxy: http.ProxyFromEnvironment, HandshakeTimeout: 5 * time.Second, TLSClientConfig: &tls.Config{ Certificates: []tls.Certificate{validCert}, InsecureSkipVerify: true, - CipherSuites: ciperSuites, + CipherSuites: cert.CiperSuites, }, Subprotocols: []string{shipWebsocketSubProtocol}, } @@ -330,10 +351,13 @@ func (s *HubSuite) Test_ServeHTTP() { assert.Nil(s.T(), err) con.Close() + server.CloseClientConnections() server.Close() + + time.Sleep(time.Second) } -func (s *HubSuite) Test_ConnectFoundService() { +func (s *HubSuite) Test_ConnectFoundService_01() { service := s.sut.ServiceForSKI(s.remoteSki) err := s.sut.connectFoundService(service, "localhost", "80") @@ -346,41 +370,58 @@ func (s *HubSuite) Test_ConnectFoundService() { err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) assert.NotNil(s.T(), err) + server.CloseClientConnections() server.Close() - server = httptest.NewUnstartedServer(s.sut) - invalidCert, _ := CreateInvalidCertificate("unit", "org", "DE", "CN") + time.Sleep(time.Second) +} + +func (s *HubSuite) Test_ConnectFoundService_02() { + service := s.sut.ServiceForSKI(s.remoteSki) + + server := httptest.NewUnstartedServer(s.sut) + invalidCert, _ := createInvalidCertificate("unit", "org", "DE", "CN") server.TLS = &tls.Config{ Certificates: []tls.Certificate{invalidCert}, ClientAuth: tls.RequireAnyClientCert, - CipherSuites: ciperSuites, + CipherSuites: cert.CiperSuites, InsecureSkipVerify: true, } server.StartTLS() - url, err = url.Parse(server.URL) + url, err := url.Parse(server.URL) assert.Nil(s.T(), err) err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) assert.NotNil(s.T(), err) + server.CloseClientConnections() server.Close() - server = httptest.NewUnstartedServer(s.sut) + time.Sleep(time.Second) +} + +func (s *HubSuite) Test_ConnectFoundService_03() { + service := s.sut.ServiceForSKI(s.remoteSki) + + server := httptest.NewUnstartedServer(s.sut) server.TLS = &tls.Config{ - Certificates: []tls.Certificate{s.sut.configuration.certificate}, + Certificates: []tls.Certificate{s.sut.configuration.Certificate()}, ClientAuth: tls.RequireAnyClientCert, - CipherSuites: ciperSuites, + CipherSuites: cert.CiperSuites, InsecureSkipVerify: true, } server.StartTLS() - url, err = url.Parse(server.URL) + url, err := url.Parse(server.URL) assert.Nil(s.T(), err) err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) assert.NotNil(s.T(), err) + time.Sleep(time.Second) + + server.CloseClientConnections() server.Close() } @@ -400,7 +441,7 @@ func (s *HubSuite) Test_KeepThisConnection() { } func (s *HubSuite) Test_prepareConnectionInitiation() { - entry := &MdnsEntry{ + entry := &api.MdnsEntry{ Ski: s.remoteSki, Host: "somehost", } @@ -415,7 +456,7 @@ func (s *HubSuite) Test_prepareConnectionInitiation() { s.sut.prepareConnectionInitation(s.remoteSki, 0, entry) s.sut.RegisterRemoteSKI(s.remoteSki, false) - service.ConnectionStateDetail().SetState(ConnectionStateQueued) + service.ConnectionStateDetail().SetState(api.ConnectionStateQueued) counter = s.sut.increaseConnectionAttemptCounter(s.remoteSki) assert.Equal(s.T(), 0, counter) @@ -424,7 +465,7 @@ func (s *HubSuite) Test_prepareConnectionInitiation() { } func (s *HubSuite) Test_InitiateConnection() { - entry := &MdnsEntry{ + entry := &api.MdnsEntry{ Ski: s.remoteSki, Host: "somehost", } @@ -439,7 +480,7 @@ func (s *HubSuite) Test_InitiateConnection() { assert.Equal(s.T(), false, result) s.sut.RegisterRemoteSKI(s.remoteSki, true) - service.ConnectionStateDetail().SetState(ConnectionStateQueued) + service.ConnectionStateDetail().SetState(api.ConnectionStateQueued) result = s.sut.initateConnection(service, entry) assert.Equal(s.T(), false, result) @@ -522,19 +563,19 @@ func (s *HubSuite) Test_ReportMdnsEntries() { testski1 := "test1" testski2 := "test2" - entries := make(map[string]*MdnsEntry) + entries := make(map[string]*api.MdnsEntry) s.serviceProvider.EXPECT().VisibleMDNSRecordsUpdated(gomock.Any()).AnyTimes() s.sut.ReportMdnsEntries(entries) - entries[testski1] = &MdnsEntry{ + entries[testski1] = &api.MdnsEntry{ Ski: testski1, } service1 := s.sut.ServiceForSKI(testski1) service1.Trusted = true service1.IPv4 = "127.0.0.1" - entries[testski2] = &MdnsEntry{ + entries[testski2] = &api.MdnsEntry{ Ski: testski2, } service2 := s.sut.ServiceForSKI(testski2) @@ -543,3 +584,58 @@ func (s *HubSuite) Test_ReportMdnsEntries() { s.sut.ReportMdnsEntries(entries) } + +func createInvalidCertificate(organizationalUnit, organization, country, commonName string) (tls.Certificate, error) { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return tls.Certificate{}, err + } + + // Create the EEBUS service SKI using the private key + asn1, err := x509.MarshalECPrivateKey(privateKey) + if err != nil { + return tls.Certificate{}, err + } + // SHIP 12.2: Required to be created according to RFC 3280 4.2.1.2 + ski := sha1.Sum(asn1) + + subject := pkix.Name{ + OrganizationalUnit: []string{organizationalUnit}, + Organization: []string{organization}, + Country: []string{country}, + CommonName: commonName, + } + + // Create a random serial big int value + maxValue := new(big.Int) + maxValue.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxValue, big.NewInt(1)) + serialNumber, err := rand.Int(rand.Reader, maxValue) + if err != nil { + return tls.Certificate{}, err + } + + template := x509.Certificate{ + SignatureAlgorithm: x509.ECDSAWithSHA256, + SerialNumber: serialNumber, + Subject: subject, + NotBefore: time.Now(), // Valid starting now + NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10), // Valid for 10 years + KeyUsage: x509.KeyUsageDigitalSignature, + BasicConstraintsValid: true, + IsCA: true, + SubjectKeyId: ski[:19], + } + + certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) + if err != nil { + return tls.Certificate{}, err + } + + tlsCertificate := tls.Certificate{ + Certificate: [][]byte{certBytes}, + PrivateKey: privateKey, + SupportedSignatureAlgorithms: []tls.SignatureScheme{tls.ECDSAWithP256AndSHA256}, + } + + return tlsCertificate, nil +} diff --git a/service/mdns.go b/service/mdns.go index 21216187..32ce5a37 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -9,28 +9,15 @@ import ( "sync" "syscall" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/service/mdns" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/mdns" "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/logging" "github.com/holoplot/go-avahi" ) -type MdnsEntry struct { - Name string - Ski string - Identifier string // mandatory - Path string // mandatory - Register bool // mandatory - Brand string // optional - Type string // optional - Model string // optional - Host string // mandatory - Port int // mandatory - Addresses []net.IP // mandatory -} - type mdnsManager struct { - configuration *Configuration + configuration *api.Configuration ski string isAnnounced bool @@ -39,10 +26,10 @@ type mdnsManager struct { cancelChan chan bool // the currently available mDNS entries with the SKI as the key in the map - entries map[string]*MdnsEntry + entries map[string]*api.MdnsEntry // the registered callback, only connectionsHub is using this - searchDelegate MdnsSearch + searchDelegate api.MdnsSearch mdnsProvider mdns.MdnsProvider @@ -50,11 +37,11 @@ type mdnsManager struct { entriesMux sync.Mutex } -func newMDNS(ski string, configuration *Configuration) *mdnsManager { +func newMDNS(ski string, configuration *api.Configuration) *mdnsManager { m := &mdnsManager{ ski: ski, configuration: configuration, - entries: make(map[string]*MdnsEntry), + entries: make(map[string]*api.MdnsEntry), cancelChan: make(chan bool), } @@ -66,10 +53,10 @@ func (m *mdnsManager) interfaces() ([]net.Interface, []int32, error) { var ifaces []net.Interface var ifaceIndexes []int32 - if len(m.configuration.interfaces) > 0 { - ifaces = make([]net.Interface, len(m.configuration.interfaces)) - ifaceIndexes = make([]int32, len(m.configuration.interfaces)) - for i, ifaceName := range m.configuration.interfaces { + if len(m.configuration.Interfaces()) > 0 { + ifaces = make([]net.Interface, len(m.configuration.Interfaces())) + ifaceIndexes = make([]int32, len(m.configuration.Interfaces())) + for i, ifaceName := range m.configuration.Interfaces() { iface, err := net.InterfaceByName(ifaceName) if err != nil { return nil, nil, err @@ -87,7 +74,7 @@ func (m *mdnsManager) interfaces() ([]net.Interface, []int32, error) { return ifaces, ifaceIndexes, nil } -var _ MdnsService = (*mdnsManager)(nil) +var _ api.MdnsService = (*mdnsManager)(nil) func (m *mdnsManager) SetupMdnsService() error { ifaces, ifaceIndexes, err := m.interfaces() @@ -139,17 +126,17 @@ func (m *mdnsManager) AnnounceMdnsEntry() error { "path=" + shipWebsocketPath, "id=" + serviceIdentifier, "ski=" + m.ski, - "brand=" + m.configuration.deviceBrand, - "model=" + m.configuration.deviceModel, - "type=" + string(m.configuration.deviceType), - "register=" + fmt.Sprintf("%v", m.configuration.registerAutoAccept), + "brand=" + m.configuration.DeviceBrand(), + "model=" + m.configuration.DeviceModel(), + "type=" + string(m.configuration.DeviceType()), + "register=" + fmt.Sprintf("%v", m.configuration.RegisterAutoAccept()), } logging.Log().Debug("mdns: announce") serviceName := m.configuration.MdnsServiceName() - if err := m.mdnsProvider.Announce(serviceName, m.configuration.port, txt); err != nil { + if err := m.mdnsProvider.Announce(serviceName, m.configuration.Port(), txt); err != nil { logging.Log().Debug("mdns: failure announcing service", err) return err } @@ -187,28 +174,28 @@ func (m *mdnsManager) setIsSearchingServices(enable bool) { m.isSearchingServices = enable } -func (m *mdnsManager) mdnsEntries() map[string]*MdnsEntry { +func (m *mdnsManager) mdnsEntries() map[string]*api.MdnsEntry { m.entriesMux.Lock() defer m.entriesMux.Unlock() return m.entries } -func (m *mdnsManager) copyMdnsEntries() map[string]*MdnsEntry { +func (m *mdnsManager) copyMdnsEntries() map[string]*api.MdnsEntry { m.entriesMux.Lock() defer m.entriesMux.Unlock() - mdnsEntries := make(map[string]*MdnsEntry) + mdnsEntries := make(map[string]*api.MdnsEntry) for k, v := range m.entries { - newEntry := &MdnsEntry{} - util.DeepCopy[*MdnsEntry](v, newEntry) + newEntry := &api.MdnsEntry{} + util.DeepCopy[*api.MdnsEntry](v, newEntry) mdnsEntries[k] = newEntry } return mdnsEntries } -func (m *mdnsManager) mdnsEntry(ski string) (*MdnsEntry, bool) { +func (m *mdnsManager) mdnsEntry(ski string) (*api.MdnsEntry, bool) { m.entriesMux.Lock() defer m.entriesMux.Unlock() @@ -216,7 +203,7 @@ func (m *mdnsManager) mdnsEntry(ski string) (*MdnsEntry, bool) { return entry, ok } -func (m *mdnsManager) setMdnsEntry(ski string, entry *MdnsEntry) { +func (m *mdnsManager) setMdnsEntry(ski string, entry *api.MdnsEntry) { m.entriesMux.Lock() defer m.entriesMux.Unlock() @@ -231,7 +218,7 @@ func (m *mdnsManager) removeMdnsEntry(ski string) { } // Register a callback to be invoked for found mDNS entries -func (m *mdnsManager) RegisterMdnsSearch(cb MdnsSearch) { +func (m *mdnsManager) RegisterMdnsSearch(cb api.MdnsSearch) { m.mux.Lock() if m.searchDelegate != cb { m.searchDelegate = cb @@ -256,7 +243,7 @@ func (m *mdnsManager) RegisterMdnsSearch(cb MdnsSearch) { } // Remove a callback for found mDNS entries and stop searching if no callbacks are left -func (m *mdnsManager) UnregisterMdnsSearch(cb MdnsSearch) { +func (m *mdnsManager) UnregisterMdnsSearch(cb api.MdnsSearch) { m.mux.Lock() defer m.mux.Unlock() @@ -375,7 +362,7 @@ func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host st m.setMdnsEntry(ski, entry) } else if !exists && !remove { // new - newEntry := &MdnsEntry{ + newEntry := &api.MdnsEntry{ Name: name, Ski: ski, Identifier: identifier, diff --git a/service/mdns/mocks/MdnsProvider.go b/service/mdns/mocks/MdnsProvider.go deleted file mode 100644 index 26a3740d..00000000 --- a/service/mdns/mocks/MdnsProvider.go +++ /dev/null @@ -1,79 +0,0 @@ -// Code generated by mockery v2.39.1. DO NOT EDIT. - -package mocks - -import ( - net "net" - - mock "github.com/stretchr/testify/mock" -) - -// MdnsProvider is an autogenerated mock type for the MdnsProvider type -type MdnsProvider struct { - mock.Mock -} - -// Announce provides a mock function with given fields: serviceName, port, txt -func (_m *MdnsProvider) Announce(serviceName string, port int, txt []string) error { - ret := _m.Called(serviceName, port, txt) - - if len(ret) == 0 { - panic("no return value specified for Announce") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, int, []string) error); ok { - r0 = rf(serviceName, port, txt) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CheckAvailability provides a mock function with given fields: -func (_m *MdnsProvider) CheckAvailability() bool { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for CheckAvailability") - } - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// ResolveEntries provides a mock function with given fields: cancelChan, callback -func (_m *MdnsProvider) ResolveEntries(cancelChan chan bool, callback func(map[string]string, string, string, []net.IP, int, bool)) { - _m.Called(cancelChan, callback) -} - -// Shutdown provides a mock function with given fields: -func (_m *MdnsProvider) Shutdown() { - _m.Called() -} - -// Unannounce provides a mock function with given fields: -func (_m *MdnsProvider) Unannounce() { - _m.Called() -} - -// NewMdnsProvider creates a new instance of MdnsProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMdnsProvider(t interface { - mock.TestingT - Cleanup(func()) -}) *MdnsProvider { - mock := &MdnsProvider{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/service/mdns_test.go b/service/mdns_test.go index bee6dc0e..db5c5bb9 100644 --- a/service/mdns_test.go +++ b/service/mdns_test.go @@ -5,13 +5,14 @@ import ( "testing" "time" - mdnsmocks "github.com/enbility/eebus-go/service/mdns/mocks" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" + "github.com/enbility/eebus-go/mocks" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - gomock "go.uber.org/mock/gomock" ) func TestMdnsSuite(t *testing.T) { @@ -23,31 +24,29 @@ type MdnsSuite struct { sut *mdnsManager - config *Configuration + config *api.Configuration - mdnsService *MockMdnsService - mdnsSearch *MockMdnsSearch - mdnsProvider *mdnsmocks.MdnsProvider + mdnsService *mocks.MdnsService + mdnsSearch *mocks.MdnsSearch + mdnsProvider *mocks.MdnsProvider } func (s *MdnsSuite) SetupSuite() {} func (s *MdnsSuite) TearDownTest() {} func (s *MdnsSuite) BeforeTest(suiteName, testName string) { - ctrl := gomock.NewController(s.T()) + s.mdnsService = mocks.NewMdnsService(s.T()) - s.mdnsService = NewMockMdnsService(ctrl) + s.mdnsSearch = mocks.NewMdnsSearch(s.T()) + s.mdnsSearch.EXPECT().ReportMdnsEntries(mock.Anything).Maybe() - s.mdnsSearch = NewMockMdnsSearch(ctrl) - s.mdnsSearch.EXPECT().ReportMdnsEntries(gomock.Any()).AnyTimes() - - s.mdnsProvider = mdnsmocks.NewMdnsProvider(s.T()) + s.mdnsProvider = mocks.NewMdnsProvider(s.T()) s.mdnsProvider.On("ResolveEntries", mock.Anything, mock.Anything).Maybe().Return() s.mdnsProvider.On("Shutdown").Maybe().Return() - certificate, _ := CreateCertificate("unit", "org", "DE", "CN") + certificate, _ := cert.CreateCertificate("unit", "org", "DE", "CN") - s.config, _ = NewConfiguration( + s.config, _ = api.NewConfiguration( "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, []model.EntityTypeType{model.EntityTypeTypeCEM}, 4729, certificate, 230.0, time.Second*4) @@ -73,12 +72,12 @@ func (s *MdnsSuite) Test_SetupMdnsService() { // we don't have access to iface names on CI if !util.IsRunningOnCI() { - s.config.interfaces = []string{ifaces[0].Name} + s.config.SetInterfaces([]string{ifaces[0].Name}) err = s.sut.SetupMdnsService() assert.Nil(s.T(), err) } - s.config.interfaces = []string{"noifacename"} + s.config.SetInterfaces([]string{"noifacename"}) err = s.sut.SetupMdnsService() assert.NotNil(s.T(), err) @@ -99,7 +98,7 @@ func (s *MdnsSuite) Test_MdnsEntry() { entries := s.sut.mdnsEntries() assert.Equal(s.T(), 0, len(entries)) - entry := &MdnsEntry{ + entry := &api.MdnsEntry{ Ski: testSki, } @@ -132,7 +131,7 @@ func (s *MdnsSuite) Test_MdnsSearch() { testSki := "test" - entry := &MdnsEntry{ + entry := &api.MdnsEntry{ Ski: testSki, } s.sut.setMdnsEntry(testSki, entry) diff --git a/service/mock_hub_test.go b/service/mock_hub_test.go deleted file mode 100644 index e8c68eb3..00000000 --- a/service/mock_hub_test.go +++ /dev/null @@ -1,260 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/enbility/eebus-go/service (interfaces: ServiceProvider,ConnectionsHub) -// -// Generated by this command: -// -// mockgen -destination=mock_hub_test.go -package=service github.com/enbility/eebus-go/service ServiceProvider,ConnectionsHub -// - -// Package service is a generated GoMock package. -package service - -import ( - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockServiceProvider is a mock of ServiceProvider interface. -type MockServiceProvider struct { - ctrl *gomock.Controller - recorder *MockServiceProviderMockRecorder -} - -// MockServiceProviderMockRecorder is the mock recorder for MockServiceProvider. -type MockServiceProviderMockRecorder struct { - mock *MockServiceProvider -} - -// NewMockServiceProvider creates a new mock instance. -func NewMockServiceProvider(ctrl *gomock.Controller) *MockServiceProvider { - mock := &MockServiceProvider{ctrl: ctrl} - mock.recorder = &MockServiceProviderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockServiceProvider) EXPECT() *MockServiceProviderMockRecorder { - return m.recorder -} - -// AllowWaitingForTrust mocks base method. -func (m *MockServiceProvider) AllowWaitingForTrust(arg0 string) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AllowWaitingForTrust", arg0) - ret0, _ := ret[0].(bool) - return ret0 -} - -// AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. -func (mr *MockServiceProviderMockRecorder) AllowWaitingForTrust(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockServiceProvider)(nil).AllowWaitingForTrust), arg0) -} - -// RemoteSKIConnected mocks base method. -func (m *MockServiceProvider) RemoteSKIConnected(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoteSKIConnected", arg0) -} - -// RemoteSKIConnected indicates an expected call of RemoteSKIConnected. -func (mr *MockServiceProviderMockRecorder) RemoteSKIConnected(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIConnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIConnected), arg0) -} - -// RemoteSKIDisconnected mocks base method. -func (m *MockServiceProvider) RemoteSKIDisconnected(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoteSKIDisconnected", arg0) -} - -// RemoteSKIDisconnected indicates an expected call of RemoteSKIDisconnected. -func (mr *MockServiceProviderMockRecorder) RemoteSKIDisconnected(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIDisconnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIDisconnected), arg0) -} - -// ServicePairingDetailUpdate mocks base method. -func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 *ConnectionStateDetail) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ServicePairingDetailUpdate", arg0, arg1) -} - -// ServicePairingDetailUpdate indicates an expected call of ServicePairingDetailUpdate. -func (mr *MockServiceProviderMockRecorder) ServicePairingDetailUpdate(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServicePairingDetailUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServicePairingDetailUpdate), arg0, arg1) -} - -// ServiceShipIDUpdate mocks base method. -func (m *MockServiceProvider) ServiceShipIDUpdate(arg0, arg1 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ServiceShipIDUpdate", arg0, arg1) -} - -// ServiceShipIDUpdate indicates an expected call of ServiceShipIDUpdate. -func (mr *MockServiceProviderMockRecorder) ServiceShipIDUpdate(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceShipIDUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServiceShipIDUpdate), arg0, arg1) -} - -// VisibleMDNSRecordsUpdated mocks base method. -func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []*MdnsEntry) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "VisibleMDNSRecordsUpdated", arg0) -} - -// VisibleMDNSRecordsUpdated indicates an expected call of VisibleMDNSRecordsUpdated. -func (mr *MockServiceProviderMockRecorder) VisibleMDNSRecordsUpdated(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleMDNSRecordsUpdated", reflect.TypeOf((*MockServiceProvider)(nil).VisibleMDNSRecordsUpdated), arg0) -} - -// MockConnectionsHub is a mock of ConnectionsHub interface. -type MockConnectionsHub struct { - ctrl *gomock.Controller - recorder *MockConnectionsHubMockRecorder -} - -// MockConnectionsHubMockRecorder is the mock recorder for MockConnectionsHub. -type MockConnectionsHubMockRecorder struct { - mock *MockConnectionsHub -} - -// NewMockConnectionsHub creates a new mock instance. -func NewMockConnectionsHub(ctrl *gomock.Controller) *MockConnectionsHub { - mock := &MockConnectionsHub{ctrl: ctrl} - mock.recorder = &MockConnectionsHubMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockConnectionsHub) EXPECT() *MockConnectionsHubMockRecorder { - return m.recorder -} - -// CancelPairingWithSKI mocks base method. -func (m *MockConnectionsHub) CancelPairingWithSKI(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "CancelPairingWithSKI", arg0) -} - -// CancelPairingWithSKI indicates an expected call of CancelPairingWithSKI. -func (mr *MockConnectionsHubMockRecorder) CancelPairingWithSKI(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelPairingWithSKI", reflect.TypeOf((*MockConnectionsHub)(nil).CancelPairingWithSKI), arg0) -} - -// DisconnectSKI mocks base method. -func (m *MockConnectionsHub) DisconnectSKI(arg0, arg1 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "DisconnectSKI", arg0, arg1) -} - -// DisconnectSKI indicates an expected call of DisconnectSKI. -func (mr *MockConnectionsHubMockRecorder) DisconnectSKI(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisconnectSKI", reflect.TypeOf((*MockConnectionsHub)(nil).DisconnectSKI), arg0, arg1) -} - -// InitiatePairingWithSKI mocks base method. -func (m *MockConnectionsHub) InitiatePairingWithSKI(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "InitiatePairingWithSKI", arg0) -} - -// InitiatePairingWithSKI indicates an expected call of InitiatePairingWithSKI. -func (mr *MockConnectionsHubMockRecorder) InitiatePairingWithSKI(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitiatePairingWithSKI", reflect.TypeOf((*MockConnectionsHub)(nil).InitiatePairingWithSKI), arg0) -} - -// PairingDetailForSki mocks base method. -func (m *MockConnectionsHub) PairingDetailForSki(arg0 string) *ConnectionStateDetail { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PairingDetailForSki", arg0) - ret0, _ := ret[0].(*ConnectionStateDetail) - return ret0 -} - -// PairingDetailForSki indicates an expected call of PairingDetailForSki. -func (mr *MockConnectionsHubMockRecorder) PairingDetailForSki(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PairingDetailForSki", reflect.TypeOf((*MockConnectionsHub)(nil).PairingDetailForSki), arg0) -} - -// RegisterRemoteSKI mocks base method. -func (m *MockConnectionsHub) RegisterRemoteSKI(arg0 string, arg1 bool) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RegisterRemoteSKI", arg0, arg1) -} - -// RegisterRemoteSKI indicates an expected call of RegisterRemoteSKI. -func (mr *MockConnectionsHubMockRecorder) RegisterRemoteSKI(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRemoteSKI", reflect.TypeOf((*MockConnectionsHub)(nil).RegisterRemoteSKI), arg0, arg1) -} - -// ServiceForSKI mocks base method. -func (m *MockConnectionsHub) ServiceForSKI(arg0 string) *ServiceDetails { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServiceForSKI", arg0) - ret0, _ := ret[0].(*ServiceDetails) - return ret0 -} - -// ServiceForSKI indicates an expected call of ServiceForSKI. -func (mr *MockConnectionsHubMockRecorder) ServiceForSKI(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceForSKI", reflect.TypeOf((*MockConnectionsHub)(nil).ServiceForSKI), arg0) -} - -// Shutdown mocks base method. -func (m *MockConnectionsHub) Shutdown() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Shutdown") -} - -// Shutdown indicates an expected call of Shutdown. -func (mr *MockConnectionsHubMockRecorder) Shutdown() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockConnectionsHub)(nil).Shutdown)) -} - -// Start mocks base method. -func (m *MockConnectionsHub) Start() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Start") -} - -// Start indicates an expected call of Start. -func (mr *MockConnectionsHubMockRecorder) Start() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockConnectionsHub)(nil).Start)) -} - -// StartBrowseMdnsSearch mocks base method. -func (m *MockConnectionsHub) StartBrowseMdnsSearch() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "StartBrowseMdnsSearch") -} - -// StartBrowseMdnsSearch indicates an expected call of StartBrowseMdnsSearch. -func (mr *MockConnectionsHubMockRecorder) StartBrowseMdnsSearch() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartBrowseMdnsSearch", reflect.TypeOf((*MockConnectionsHub)(nil).StartBrowseMdnsSearch)) -} - -// StopBrowseMdnsSearch mocks base method. -func (m *MockConnectionsHub) StopBrowseMdnsSearch() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "StopBrowseMdnsSearch") -} - -// StopBrowseMdnsSearch indicates an expected call of StopBrowseMdnsSearch. -func (mr *MockConnectionsHubMockRecorder) StopBrowseMdnsSearch() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopBrowseMdnsSearch", reflect.TypeOf((*MockConnectionsHub)(nil).StopBrowseMdnsSearch)) -} diff --git a/service/mock_mdns_test.go b/service/mock_mdns_test.go deleted file mode 100644 index bf8ff6f6..00000000 --- a/service/mock_mdns_test.go +++ /dev/null @@ -1,150 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/enbility/eebus-go/service (interfaces: MdnsSearch,MdnsService) -// -// Generated by this command: -// -// mockgen -destination=mock_mdns_test.go -package=service github.com/enbility/eebus-go/service MdnsSearch,MdnsService -// - -// Package service is a generated GoMock package. -package service - -import ( - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockMdnsSearch is a mock of MdnsSearch interface. -type MockMdnsSearch struct { - ctrl *gomock.Controller - recorder *MockMdnsSearchMockRecorder -} - -// MockMdnsSearchMockRecorder is the mock recorder for MockMdnsSearch. -type MockMdnsSearchMockRecorder struct { - mock *MockMdnsSearch -} - -// NewMockMdnsSearch creates a new mock instance. -func NewMockMdnsSearch(ctrl *gomock.Controller) *MockMdnsSearch { - mock := &MockMdnsSearch{ctrl: ctrl} - mock.recorder = &MockMdnsSearchMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockMdnsSearch) EXPECT() *MockMdnsSearchMockRecorder { - return m.recorder -} - -// ReportMdnsEntries mocks base method. -func (m *MockMdnsSearch) ReportMdnsEntries(arg0 map[string]*MdnsEntry) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ReportMdnsEntries", arg0) -} - -// ReportMdnsEntries indicates an expected call of ReportMdnsEntries. -func (mr *MockMdnsSearchMockRecorder) ReportMdnsEntries(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportMdnsEntries", reflect.TypeOf((*MockMdnsSearch)(nil).ReportMdnsEntries), arg0) -} - -// MockMdnsService is a mock of MdnsService interface. -type MockMdnsService struct { - ctrl *gomock.Controller - recorder *MockMdnsServiceMockRecorder -} - -// MockMdnsServiceMockRecorder is the mock recorder for MockMdnsService. -type MockMdnsServiceMockRecorder struct { - mock *MockMdnsService -} - -// NewMockMdnsService creates a new mock instance. -func NewMockMdnsService(ctrl *gomock.Controller) *MockMdnsService { - mock := &MockMdnsService{ctrl: ctrl} - mock.recorder = &MockMdnsServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockMdnsService) EXPECT() *MockMdnsServiceMockRecorder { - return m.recorder -} - -// AnnounceMdnsEntry mocks base method. -func (m *MockMdnsService) AnnounceMdnsEntry() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AnnounceMdnsEntry") - ret0, _ := ret[0].(error) - return ret0 -} - -// AnnounceMdnsEntry indicates an expected call of AnnounceMdnsEntry. -func (mr *MockMdnsServiceMockRecorder) AnnounceMdnsEntry() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnnounceMdnsEntry", reflect.TypeOf((*MockMdnsService)(nil).AnnounceMdnsEntry)) -} - -// RegisterMdnsSearch mocks base method. -func (m *MockMdnsService) RegisterMdnsSearch(arg0 MdnsSearch) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RegisterMdnsSearch", arg0) -} - -// RegisterMdnsSearch indicates an expected call of RegisterMdnsSearch. -func (mr *MockMdnsServiceMockRecorder) RegisterMdnsSearch(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).RegisterMdnsSearch), arg0) -} - -// SetupMdnsService mocks base method. -func (m *MockMdnsService) SetupMdnsService() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetupMdnsService") - ret0, _ := ret[0].(error) - return ret0 -} - -// SetupMdnsService indicates an expected call of SetupMdnsService. -func (mr *MockMdnsServiceMockRecorder) SetupMdnsService() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupMdnsService", reflect.TypeOf((*MockMdnsService)(nil).SetupMdnsService)) -} - -// ShutdownMdnsService mocks base method. -func (m *MockMdnsService) ShutdownMdnsService() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ShutdownMdnsService") -} - -// ShutdownMdnsService indicates an expected call of ShutdownMdnsService. -func (mr *MockMdnsServiceMockRecorder) ShutdownMdnsService() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShutdownMdnsService", reflect.TypeOf((*MockMdnsService)(nil).ShutdownMdnsService)) -} - -// UnannounceMdnsEntry mocks base method. -func (m *MockMdnsService) UnannounceMdnsEntry() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "UnannounceMdnsEntry") -} - -// UnannounceMdnsEntry indicates an expected call of UnannounceMdnsEntry. -func (mr *MockMdnsServiceMockRecorder) UnannounceMdnsEntry() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnannounceMdnsEntry", reflect.TypeOf((*MockMdnsService)(nil).UnannounceMdnsEntry)) -} - -// UnregisterMdnsSearch mocks base method. -func (m *MockMdnsService) UnregisterMdnsSearch(arg0 MdnsSearch) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "UnregisterMdnsSearch", arg0) -} - -// UnregisterMdnsSearch indicates an expected call of UnregisterMdnsSearch. -func (mr *MockMdnsServiceMockRecorder) UnregisterMdnsSearch(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).UnregisterMdnsSearch), arg0) -} diff --git a/service/mock_service_test.go b/service/mock_service_test.go deleted file mode 100644 index 19ab3765..00000000 --- a/service/mock_service_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/enbility/eebus-go/service (interfaces: EEBUSServiceHandler) -// -// Generated by this command: -// -// mockgen -destination=mock_service_test.go -package=service github.com/enbility/eebus-go/service EEBUSServiceHandler -// - -// Package service is a generated GoMock package. -package service - -import ( - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockEEBUSServiceHandler is a mock of EEBUSServiceHandler interface. -type MockEEBUSServiceHandler struct { - ctrl *gomock.Controller - recorder *MockEEBUSServiceHandlerMockRecorder -} - -// MockEEBUSServiceHandlerMockRecorder is the mock recorder for MockEEBUSServiceHandler. -type MockEEBUSServiceHandlerMockRecorder struct { - mock *MockEEBUSServiceHandler -} - -// NewMockEEBUSServiceHandler creates a new mock instance. -func NewMockEEBUSServiceHandler(ctrl *gomock.Controller) *MockEEBUSServiceHandler { - mock := &MockEEBUSServiceHandler{ctrl: ctrl} - mock.recorder = &MockEEBUSServiceHandlerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockEEBUSServiceHandler) EXPECT() *MockEEBUSServiceHandlerMockRecorder { - return m.recorder -} - -// AllowWaitingForTrust mocks base method. -func (m *MockEEBUSServiceHandler) AllowWaitingForTrust(arg0 string) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AllowWaitingForTrust", arg0) - ret0, _ := ret[0].(bool) - return ret0 -} - -// AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. -func (mr *MockEEBUSServiceHandlerMockRecorder) AllowWaitingForTrust(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).AllowWaitingForTrust), arg0) -} - -// RemoteSKIConnected mocks base method. -func (m *MockEEBUSServiceHandler) RemoteSKIConnected(arg0 *EEBUSService, arg1 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoteSKIConnected", arg0, arg1) -} - -// RemoteSKIConnected indicates an expected call of RemoteSKIConnected. -func (mr *MockEEBUSServiceHandlerMockRecorder) RemoteSKIConnected(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIConnected", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).RemoteSKIConnected), arg0, arg1) -} - -// RemoteSKIDisconnected mocks base method. -func (m *MockEEBUSServiceHandler) RemoteSKIDisconnected(arg0 *EEBUSService, arg1 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoteSKIDisconnected", arg0, arg1) -} - -// RemoteSKIDisconnected indicates an expected call of RemoteSKIDisconnected. -func (mr *MockEEBUSServiceHandlerMockRecorder) RemoteSKIDisconnected(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIDisconnected", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).RemoteSKIDisconnected), arg0, arg1) -} - -// ServicePairingDetailUpdate mocks base method. -func (m *MockEEBUSServiceHandler) ServicePairingDetailUpdate(arg0 string, arg1 *ConnectionStateDetail) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ServicePairingDetailUpdate", arg0, arg1) -} - -// ServicePairingDetailUpdate indicates an expected call of ServicePairingDetailUpdate. -func (mr *MockEEBUSServiceHandlerMockRecorder) ServicePairingDetailUpdate(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServicePairingDetailUpdate", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).ServicePairingDetailUpdate), arg0, arg1) -} - -// ServiceShipIDUpdate mocks base method. -func (m *MockEEBUSServiceHandler) ServiceShipIDUpdate(arg0, arg1 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ServiceShipIDUpdate", arg0, arg1) -} - -// ServiceShipIDUpdate indicates an expected call of ServiceShipIDUpdate. -func (mr *MockEEBUSServiceHandlerMockRecorder) ServiceShipIDUpdate(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceShipIDUpdate", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).ServiceShipIDUpdate), arg0, arg1) -} - -// VisibleRemoteServicesUpdated mocks base method. -func (m *MockEEBUSServiceHandler) VisibleRemoteServicesUpdated(arg0 *EEBUSService, arg1 []RemoteService) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "VisibleRemoteServicesUpdated", arg0, arg1) -} - -// VisibleRemoteServicesUpdated indicates an expected call of VisibleRemoteServicesUpdated. -func (mr *MockEEBUSServiceHandlerMockRecorder) VisibleRemoteServicesUpdated(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleRemoteServicesUpdated", reflect.TypeOf((*MockEEBUSServiceHandler)(nil).VisibleRemoteServicesUpdated), arg0, arg1) -} diff --git a/service/service.go b/service/service.go index 25ff602c..88df8c4b 100644 --- a/service/service.go +++ b/service/service.go @@ -6,54 +6,48 @@ import ( "fmt" "sync" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) -type RemoteService struct { - Name string `json:"name"` - Ski string `json:"ski"` - Identifier string `json:"identifier"` - Brand string `json:"brand"` - Type string `json:"type"` - Model string `json:"model"` -} - // A service is the central element of an EEBUS service // including its websocket server and a zeroconf service. -type EEBUSService struct { - Configuration *Configuration +type EEBUSServiceImpl struct { + Configuration *api.Configuration // The local service details - LocalService *ServiceDetails + LocalService *api.ServiceDetails // Connection Registrations - connectionsHub ConnectionsHub + connectionsHub api.ConnectionsHub // The SPINE specific device definition - spineLocalDevice *spine.DeviceLocalImpl + spineLocalDevice spineapi.DeviceLocal - serviceHandler EEBUSServiceHandler + serviceHandler api.EEBUSServiceHandler startOnce sync.Once } // creates a new EEBUS service -func NewEEBUSService(configuration *Configuration, serviceHandler EEBUSServiceHandler) *EEBUSService { - return &EEBUSService{ +func NewEEBUSService(configuration *api.Configuration, serviceHandler api.EEBUSServiceHandler) *EEBUSServiceImpl { + return &EEBUSServiceImpl{ Configuration: configuration, serviceHandler: serviceHandler, } } -var _ ServiceProvider = (*EEBUSService)(nil) +var _ api.ServiceProvider = (*EEBUSServiceImpl)(nil) -func (s *EEBUSService) VisibleMDNSRecordsUpdated(entries []*MdnsEntry) { - var remoteServices []RemoteService +func (s *EEBUSServiceImpl) VisibleMDNSRecordsUpdated(entries []*api.MdnsEntry) { + var remoteServices []api.RemoteService for _, entry := range entries { - remoteService := RemoteService{ + remoteService := api.RemoteService{ Name: entry.Name, Ski: entry.Ski, Identifier: entry.Identifier, @@ -68,50 +62,52 @@ func (s *EEBUSService) VisibleMDNSRecordsUpdated(entries []*MdnsEntry) { } // report a connection to a SKI -func (s *EEBUSService) RemoteSKIConnected(ski string) { +func (s *EEBUSServiceImpl) RemoteSKIConnected(ski string) { s.serviceHandler.RemoteSKIConnected(s, ski) } // report a disconnection to a SKI -func (s *EEBUSService) RemoteSKIDisconnected(ski string) { +func (s *EEBUSServiceImpl) RemoteSKIDisconnected(ski string) { s.serviceHandler.RemoteSKIDisconnected(s, ski) } // Provides the SHIP ID the remote service reported during the handshake process -func (s *EEBUSService) ServiceShipIDUpdate(ski string, shipdID string) { +func (s *EEBUSServiceImpl) ServiceShipIDUpdate(ski string, shipdID string) { s.serviceHandler.ServiceShipIDUpdate(ski, shipdID) } // Provides the current pairing state for the remote service // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process -func (s *EEBUSService) ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) { +func (s *EEBUSServiceImpl) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { s.serviceHandler.ServicePairingDetailUpdate(ski, detail) } // return if the user is still able to trust the connection -func (s *EEBUSService) AllowWaitingForTrust(ski string) bool { +func (s *EEBUSServiceImpl) AllowWaitingForTrust(ski string) bool { return s.serviceHandler.AllowWaitingForTrust(ski) } +var _ api.EEBUSService = (*EEBUSServiceImpl)(nil) + // Get the current pairing details for a given SKI -func (s *EEBUSService) PairingDetailForSki(ski string) *ConnectionStateDetail { +func (s *EEBUSServiceImpl) PairingDetailForSki(ski string) *api.ConnectionStateDetail { return s.connectionsHub.PairingDetailForSki(ski) } // Starts browsing for any EEBUS mDNS entry -func (s *EEBUSService) StartBrowseMdnsEntries() { +func (s *EEBUSServiceImpl) StartBrowseMdnsEntries() { s.connectionsHub.StartBrowseMdnsSearch() } // Stop brwosing for any EEBUS mDNS entry -func (s *EEBUSService) StopBrowseMdnsEntries() { +func (s *EEBUSServiceImpl) StopBrowseMdnsEntries() { s.connectionsHub.StopBrowseMdnsSearch() } // Sets a custom logging implementation // By default NoLogging is used, so no logs are printed -func (s *EEBUSService) SetLogging(logger logging.Logging) { +func (s *EEBUSServiceImpl) SetLogging(logger logging.Logging) { if logger == nil { return } @@ -119,23 +115,19 @@ func (s *EEBUSService) SetLogging(logger logging.Logging) { } // Starts the service by initializeing mDNS and the server. -func (s *EEBUSService) Setup() error { - if s.Configuration.port == 0 { - s.Configuration.port = defaultPort - } - +func (s *EEBUSServiceImpl) Setup() error { sd := s.Configuration - if len(sd.certificate.Certificate) == 0 { + if len(sd.Certificate().Certificate) == 0 { return errors.New("missing certificate") } - leaf, err := x509.ParseCertificate(sd.certificate.Certificate[0]) + leaf, err := x509.ParseCertificate(sd.Certificate().Certificate[0]) if err != nil { return err } - ski, err := skiFromCertificate(leaf) + ski, err := cert.SkiFromCertificate(leaf) if err != nil { return err } @@ -148,40 +140,40 @@ func (s *EEBUSService) Setup() error { // The originator's unique ID // I assume those two to mean the same. // TODO: clarify - s.LocalService = NewServiceDetails(ski) + s.LocalService = api.NewServiceDetails(ski) s.LocalService.ShipID = sd.Identifier() - s.LocalService.DeviceType = sd.deviceType - s.LocalService.RegisterAutoAccept = sd.registerAutoAccept + s.LocalService.DeviceType = sd.DeviceType() + s.LocalService.RegisterAutoAccept = sd.RegisterAutoAccept() logging.Log().Info("Local SKI: ", ski) - vendor := sd.vendorCode + vendor := sd.VendorCode() if vendor == "" { - vendor = sd.deviceBrand + vendor = sd.DeviceBrand() } - serial := sd.deviceSerialNumber + serial := sd.DeviceSerialNumber() if serial != "" { serial = fmt.Sprintf("-%s", serial) } // Create the SPINE device address, according to Protocol Specification 7.1.1.2 - deviceAdress := fmt.Sprintf("d:_i:%s_%s%s", vendor, sd.deviceModel, serial) + deviceAdress := fmt.Sprintf("d:_i:%s_%s%s", vendor, sd.DeviceModel(), serial) // Create the local SPINE device s.spineLocalDevice = spine.NewDeviceLocalImpl( - sd.deviceBrand, - sd.deviceModel, - sd.deviceSerialNumber, + sd.DeviceBrand(), + sd.DeviceModel(), + sd.DeviceSerialNumber(), sd.Identifier(), deviceAdress, - sd.deviceType, - sd.featureSet, - sd.heartbeatTimeout, + sd.DeviceType(), + sd.FeatureSet(), + sd.HeartbeatTimeout(), ) // Create the device entities and add it to the SPINE device - for _, entityType := range sd.entityTypes { + for _, entityType := range sd.EntityTypes() { entityAddressId := model.AddressEntityType(len(s.spineLocalDevice.Entities())) entityAddress := []model.AddressEntityType{entityAddressId} entity := spine.NewEntityLocalImpl(s.spineLocalDevice, entityType, entityAddress) @@ -198,44 +190,44 @@ func (s *EEBUSService) Setup() error { } // Starts the service -func (s *EEBUSService) Start() { +func (s *EEBUSServiceImpl) Start() { s.startOnce.Do(func() { s.connectionsHub.Start() }) } // Shutdown all services and stop the server. -func (s *EEBUSService) Shutdown() { +func (s *EEBUSServiceImpl) Shutdown() { // Shut down all running connections s.connectionsHub.Shutdown() } -func (s *EEBUSService) LocalDevice() *spine.DeviceLocalImpl { +func (s *EEBUSServiceImpl) LocalDevice() spineapi.DeviceLocal { return s.spineLocalDevice } // Returns the Service detail of a given remote SKI -func (s *EEBUSService) RemoteServiceForSKI(ski string) *ServiceDetails { +func (s *EEBUSServiceImpl) RemoteServiceForSKI(ski string) *api.ServiceDetails { return s.connectionsHub.ServiceForSKI(ski) } // Sets the SKI as being paired or not // and connect it if paired and not currently being connected -func (s *EEBUSService) RegisterRemoteSKI(ski string, enable bool) { +func (s *EEBUSServiceImpl) RegisterRemoteSKI(ski string, enable bool) { s.connectionsHub.RegisterRemoteSKI(ski, enable) } // Triggers the pairing process for a SKI -func (s *EEBUSService) InitiatePairingWithSKI(ski string) { +func (s *EEBUSServiceImpl) InitiatePairingWithSKI(ski string) { s.connectionsHub.InitiatePairingWithSKI(ski) } // Cancels the pairing process for a SKI -func (s *EEBUSService) CancelPairingWithSKI(ski string) { +func (s *EEBUSServiceImpl) CancelPairingWithSKI(ski string) { s.connectionsHub.CancelPairingWithSKI(ski) } // Close a connection to a remote SKI -func (s *EEBUSService) DisconnectSKI(ski string, reason string) { +func (s *EEBUSServiceImpl) DisconnectSKI(ski string, reason string) { s.connectionsHub.DisconnectSKI(ski, reason) } diff --git a/service/service_test.go b/service/service_test.go index b14fc5b7..c144aabe 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -5,12 +5,15 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/logging/mocks" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" + "github.com/enbility/eebus-go/mocks" + "github.com/enbility/ship-go/logging" + shipmocks "github.com/enbility/ship-go/mocks" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - gomock "go.uber.org/mock/gomock" ) func TestServiceSuite(t *testing.T) { @@ -20,29 +23,24 @@ func TestServiceSuite(t *testing.T) { type ServiceSuite struct { suite.Suite - config *Configuration + config *api.Configuration - sut *EEBUSService + sut *EEBUSServiceImpl - serviceHandler *MockEEBUSServiceHandler - conHub *MockConnectionsHub - logging *mocks.Logging + serviceHandler *mocks.EEBUSServiceHandler + conHub *mocks.ConnectionsHub + logging *shipmocks.Logging } -func (s *ServiceSuite) SetupSuite() {} -func (s *ServiceSuite) TearDownTest() {} - func (s *ServiceSuite) BeforeTest(suiteName, testName string) { - ctrl := gomock.NewController(s.T()) - - s.serviceHandler = NewMockEEBUSServiceHandler(ctrl) + s.serviceHandler = mocks.NewEEBUSServiceHandler(s.T()) - s.conHub = NewMockConnectionsHub(ctrl) + s.conHub = mocks.NewConnectionsHub(s.T()) - s.logging = mocks.NewLogging(s.T()) + s.logging = shipmocks.NewLogging(s.T()) certificate := tls.Certificate{} - s.config, _ = NewConfiguration( + s.config, _ = api.NewConfiguration( "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, []model.EntityTypeType{model.EntityTypeTypeCEM}, 4729, certificate, 230.0, time.Second*4) @@ -52,28 +50,28 @@ func (s *ServiceSuite) BeforeTest(suiteName, testName string) { func (s *ServiceSuite) Test_EEBUSHandler() { testSki := "test" - entry := &MdnsEntry{ + entry := &api.MdnsEntry{ Ski: testSki, } - entries := []*MdnsEntry{entry} - s.serviceHandler.EXPECT().VisibleRemoteServicesUpdated(gomock.Any(), gomock.Any()) + entries := []*api.MdnsEntry{entry} + s.serviceHandler.EXPECT().VisibleRemoteServicesUpdated(mock.Anything, mock.Anything).Return() s.sut.VisibleMDNSRecordsUpdated(entries) - s.serviceHandler.EXPECT().RemoteSKIConnected(gomock.Any(), gomock.Any()) + s.serviceHandler.EXPECT().RemoteSKIConnected(mock.Anything, mock.Anything).Return() s.sut.RemoteSKIConnected(testSki) - s.serviceHandler.EXPECT().RemoteSKIDisconnected(gomock.Any(), gomock.Any()) + s.serviceHandler.EXPECT().RemoteSKIDisconnected(mock.Anything, mock.Anything).Return() s.sut.RemoteSKIDisconnected(testSki) - s.serviceHandler.EXPECT().ServiceShipIDUpdate(gomock.Any(), gomock.Any()) + s.serviceHandler.EXPECT().ServiceShipIDUpdate(mock.Anything, mock.Anything).Return() s.sut.ServiceShipIDUpdate(testSki, "shipid") - s.serviceHandler.EXPECT().ServicePairingDetailUpdate(gomock.Any(), gomock.Any()) - detail := &ConnectionStateDetail{} + s.serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return() + detail := &api.ConnectionStateDetail{} s.sut.ServicePairingDetailUpdate(testSki, detail) - s.serviceHandler.EXPECT().AllowWaitingForTrust(gomock.Any()).Return(true) + s.serviceHandler.EXPECT().AllowWaitingForTrust(mock.Anything).Return(true) result := s.sut.AllowWaitingForTrust(testSki) assert.Equal(s.T(), true, result) @@ -84,29 +82,29 @@ func (s *ServiceSuite) Test_ConnectionsHub() { s.sut.connectionsHub = s.conHub - s.conHub.EXPECT().PairingDetailForSki(gomock.Any()) + s.conHub.EXPECT().PairingDetailForSki(mock.Anything).Return(nil) s.sut.PairingDetailForSki(testSki) - s.conHub.EXPECT().StartBrowseMdnsSearch() + s.conHub.EXPECT().StartBrowseMdnsSearch().Return() s.sut.StartBrowseMdnsEntries() - s.conHub.EXPECT().StopBrowseMdnsSearch() + s.conHub.EXPECT().StopBrowseMdnsSearch().Return() s.sut.StopBrowseMdnsEntries() - s.conHub.EXPECT().ServiceForSKI(gomock.Any()) + s.conHub.EXPECT().ServiceForSKI(mock.Anything).Return(nil) details := s.sut.RemoteServiceForSKI(testSki) assert.Nil(s.T(), details) - s.conHub.EXPECT().RegisterRemoteSKI(gomock.Any(), gomock.Any()) + s.conHub.EXPECT().RegisterRemoteSKI(mock.Anything, mock.Anything).Return() s.sut.RegisterRemoteSKI(testSki, true) - s.conHub.EXPECT().InitiatePairingWithSKI(gomock.Any()) + s.conHub.EXPECT().InitiatePairingWithSKI(mock.Anything).Return() s.sut.InitiatePairingWithSKI(testSki) - s.conHub.EXPECT().CancelPairingWithSKI(gomock.Any()) + s.conHub.EXPECT().CancelPairingWithSKI(mock.Anything).Return() s.sut.CancelPairingWithSKI(testSki) - s.conHub.EXPECT().DisconnectSKI(gomock.Any(), gomock.Any()) + s.conHub.EXPECT().DisconnectSKI(mock.Anything, mock.Anything).Return() s.sut.DisconnectSKI(testSki, "reason") } @@ -126,8 +124,9 @@ func (s *ServiceSuite) Test_Setup() { err := s.sut.Setup() assert.NotNil(s.T(), err) - s.config.certificate, err = CreateCertificate("unit", "org", "de", "cn") + certificate, err := cert.CreateCertificate("unit", "org", "de", "cn") assert.Nil(s.T(), err) + s.config.SetCertificate(certificate) err = s.sut.Setup() assert.Nil(s.T(), err) diff --git a/ship/api.go b/ship/api.go deleted file mode 100644 index ea2ec108..00000000 --- a/ship/api.go +++ /dev/null @@ -1,80 +0,0 @@ -package ship - -//go:generate mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship WebsocketDataConnection,WebsocketDataProcessing,ShipServiceDataProvider,SpineDataProcessing,SpineDataConnection -//go:generate mockery --name=WebsocketDataConnection -//go:generate mockery --name=ShipConnection - -type ShipConnection interface { - DataHandler() WebsocketDataConnection - CloseConnection(safe bool, code int, reason string) - RemoteSKI() string - ApprovePendingHandshake() - AbortPendingHandshake() - ShipHandshakeState() (ShipMessageExchangeState, error) -} - -// interface for getting service wide information -// -// implemented by connectionsHub, used by shipConnection -type ShipServiceDataProvider interface { - // check if the SKI is paired - IsRemoteServiceForSKIPaired(string) bool - - // report closing of a connection and if handshake did complete - HandleConnectionClosed(ShipConnection, bool) - - // report the ship ID provided during the handshake - ReportServiceShipID(string, string) - - // check if the user is still able to trust the connection - AllowWaitingForTrust(string) bool - - // report the updated SHIP handshake state and optional error message for a SKI - HandleShipHandshakeStateUpdate(string, ShipState) - - // report an approved handshake by a remote device - SetupRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing -} - -// Used to pass an outgoing SPINE message from a DeviceLocal to the SHIP connection -// -// Implemented by ShipConnection, used by spine DeviceLocal -type SpineDataConnection interface { - WriteSpineMessage(message []byte) -} - -// Used to pass an incoming SPINE message from a SHIP connection to the proper DeviceRemote -// -// Implemented by spine DeviceRemote, used by ShipConnection -type SpineDataProcessing interface { - HandleIncomingSpineMesssage(message []byte) -} - -// interface for handling the actual remote device data connection -// -// implemented by websocketConnection, used by ShipConnection -type WebsocketDataConnection interface { - // initialize data processing - InitDataProcessing(WebsocketDataProcessing) - - // send data via the connection to the remote device - WriteMessageToDataConnection([]byte) error - - // close the data connection - CloseDataConnection(closeCode int, reason string) - - // report if the data connection is closed and the error if availab le - IsDataConnectionClosed() (bool, error) -} - -// interface for handling incoming data -// -// implemented by shipConnection, used by websocketConnection -type WebsocketDataProcessing interface { - // called for each incoming message - HandleIncomingShipMessage([]byte) - - // called if the data connection is closed unsafe - // e.g. due to connection issues - ReportConnectionError(error) -} diff --git a/ship/connection.go b/ship/connection.go deleted file mode 100644 index 7d44df56..00000000 --- a/ship/connection.go +++ /dev/null @@ -1,411 +0,0 @@ -package ship - -import ( - "bytes" - "encoding/json" - "errors" - "strings" - "sync" - "time" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/ship/model" - shipUtil "github.com/enbility/eebus-go/ship/util" - "github.com/enbility/eebus-go/util" -) - -// A ShipConnectionImpl handles the data connection and coordinates SHIP and SPINE messages i/o -type ShipConnectionImpl struct { - // The ship connection mode of this connection - role shipRole - - // The remote SKI - remoteSKI string - - // the remote SHIP Id - remoteShipID string - - // The local SHIP ID - localShipID string - - // data provider - serviceDataProvider ShipServiceDataProvider - - // Where to pass incoming SPINE messages to - spineDataProcessing SpineDataProcessing - - // the (web socket) handler for sending messages - dataHandler WebsocketDataConnection - - // The current SHIP state - smeState ShipMessageExchangeState - - // the current error value if SHIP state is in error - smeError error - - // handles timeouts for the various states - // - // WaitForReady SHIP 13.4.4.1.3: The communication partner must send its "READY" state (or request for prolongation") before the timer expires. - // - // SendProlongationRequest SHIP 13.4.4.1.3: Local timer to request for prolongation at the communication partner in time (i.e. before the communication partner's Wait-For-Ready-Timer expires). - // - // ProlongationRequestReply SHIP 13.4.4.1.3: Detection of response timeout on prolongation request. - handshakeTimerRunning bool - handshakeTimerType timeoutTimerType - handshakeTimerStopChan chan struct{} - handshakeTimerMux sync.Mutex - - lastReceivedWaitingValue time.Duration // required for Prolong-Request-Reply-Timer - - shutdownOnce sync.Once - - mux sync.Mutex -} - -var _ ShipConnection = (*ShipConnectionImpl)(nil) - -func NewConnectionHandler( - dataProvider ShipServiceDataProvider, - dataHandler WebsocketDataConnection, - role shipRole, - localShipID, - remoteSki, - remoteShipId string) *ShipConnectionImpl { - ship := &ShipConnectionImpl{ - serviceDataProvider: dataProvider, - dataHandler: dataHandler, - role: role, - localShipID: localShipID, - remoteSKI: remoteSki, - remoteShipID: remoteShipId, - smeState: CmiStateInitStart, - smeError: nil, - } - - ship.handshakeTimerStopChan = make(chan struct{}) - - if dataHandler != nil { - dataHandler.InitDataProcessing(ship) - } - - return ship -} - -func (c *ShipConnectionImpl) RemoteSKI() string { - return c.remoteSKI -} - -func (c *ShipConnectionImpl) DataHandler() WebsocketDataConnection { - return c.dataHandler -} - -// start SHIP communication -func (c *ShipConnectionImpl) Run() { - c.handleShipMessage(false, nil) -} - -// provides the current ship state and error value if the state is in error -func (c *ShipConnectionImpl) ShipHandshakeState() (ShipMessageExchangeState, error) { - return c.getState(), c.smeError -} - -// invoked when pairing for a pending request is approved -func (c *ShipConnectionImpl) ApprovePendingHandshake() { - state := c.getState() - if state != SmeHelloStatePendingListen { - // TODO: what to do if the state is different? - - return - } - - // TODO: move this into hs_hello.go and add tests - - // HELLO_OK - c.stopHandshakeTimer() - c.setAndHandleState(SmeHelloStateReadyInit) - - // TODO: check if we need to do some validations before moving on to the next state - c.setAndHandleState(SmeHelloStateOk) -} - -// invoked when pairing for a pending request is denied -func (c *ShipConnectionImpl) AbortPendingHandshake() { - state := c.getState() - if state != SmeHelloStatePendingListen && state != SmeHelloStateReadyListen { - // TODO: what to do if the state is differnet? - - return - } - - // TODO: Move this into hs_hello.go and add tests - - c.stopHandshakeTimer() - c.setAndHandleState(SmeHelloStateAbort) -} - -// close this ship connection -func (c *ShipConnectionImpl) CloseConnection(safe bool, code int, reason string) { - c.shutdownOnce.Do(func() { - c.stopHandshakeTimer() - - // handshake is completed if approved or aborted - state := c.getState() - handshakeEnd := state == SmeStateComplete || - state == SmeHelloStateAbortDone || - state == SmeHelloStateRemoteAbortDone || - state == SmeHelloStateRejected - - // this may not be used for Connection Data Exchange is entered! - if safe && state == SmeStateComplete { - // SHIP 13.4.7: Connection Termination Announce - closeMessage := model.ConnectionClose{ - ConnectionClose: model.ConnectionCloseType{ - Phase: model.ConnectionClosePhaseTypeAnnounce, - Reason: util.Ptr(model.ConnectionCloseReasonType(reason)), - }, - } - - _ = c.sendShipModel(model.MsgTypeEnd, closeMessage) - - if state != SmeStateError { - c.serviceDataProvider.HandleConnectionClosed(c, handshakeEnd) - return - } - } - - closeCode := 4001 - if code != 0 { - closeCode = code - } - c.dataHandler.CloseDataConnection(closeCode, reason) - - c.serviceDataProvider.HandleConnectionClosed(c, handshakeEnd) - }) -} - -var _ SpineDataConnection = (*ShipConnectionImpl)(nil) - -// SpineDataConnection interface implementation -func (c *ShipConnectionImpl) WriteSpineMessage(message []byte) { - if err := c.sendSpineData(message); err != nil { - logging.Log().Debug(c.RemoteSKI, "Error sending spine message: ", err) - return - } -} - -var _ WebsocketDataProcessing = (*ShipConnectionImpl)(nil) - -func (c *ShipConnectionImpl) shipModelFromMessage(message []byte) (*model.ShipData, error) { - _, jsonData := c.parseMessage(message, true) - - // Get the datagram from the message - data := model.ShipData{} - if err := json.Unmarshal(jsonData, &data); err != nil { - logging.Log().Debug(c.RemoteSKI, "error unmarshalling message: ", err) - return nil, err - } - - if data.Data.Payload == nil { - errorMsg := "received no valid payload" - logging.Log().Debug(c.RemoteSKI, errorMsg) - return nil, errors.New(errorMsg) - } - - return &data, nil -} - -// route the incoming message to either SHIP or SPINE message handlers -func (c *ShipConnectionImpl) HandleIncomingShipMessage(message []byte) { - // Check if this is a SHIP SME or SPINE message - if !c.hasSpineDatagram(message) { - c.handleShipMessage(false, message) - return - } - - data, err := c.shipModelFromMessage(message) - if err != nil { - return - } - - if c.spineDataProcessing == nil { - return - } - - // pass the payload to the SPINE read handler - c.spineDataProcessing.HandleIncomingSpineMesssage([]byte(data.Data.Payload)) -} - -// checks wether the provided messages is a SHIP message -func (c *ShipConnectionImpl) hasSpineDatagram(message []byte) bool { - return bytes.Contains(message, []byte("datagram")) -} - -// the websocket data connection was closed from remote -func (c *ShipConnectionImpl) ReportConnectionError(err error) { - // if the handshake is aborted, a closed connection is no error - currentState := c.getState() - - // rejections are also received by sending `{"connectionHello":[{"phase":"pending"},{"waiting":60000}]}` - // and then closing the websocket connection with `4452: Node rejected by application.` - if currentState == SmeHelloStateReadyListen { - c.setState(SmeHelloStateRejected, nil) - c.CloseConnection(false, 0, "") - return - } - - if currentState == SmeHelloStateRemoteAbortDone { - // remote service should close the connection - c.CloseConnection(false, 0, "") - return - } - - if currentState == SmeHelloStateAbort || - currentState == SmeHelloStateAbortDone { - c.CloseConnection(false, 4452, "Node rejected by application") - return - } - - c.setState(SmeStateError, err) - - c.CloseConnection(false, 0, "") - - state := ShipState{ - State: SmeStateError, - Error: err, - } - c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.remoteSKI, state) -} - -const payloadPlaceholder = `{"place":"holder"}` - -func (c *ShipConnectionImpl) transformSpineDataIntoShipJson(data []byte) ([]byte, error) { - spineMsg, err := shipUtil.JsonIntoEEBUSJson(data) - if err != nil { - return nil, err - } - - payload := json.RawMessage([]byte(spineMsg)) - - // Workaround for the fact that SHIP payload is a json.RawMessage - // which would also be transformed into an array element but it shouldn't - // hence patching the payload into the message later after the SHIP - // and SPINE model are transformed independently - - // Create the message - shipMessage := model.ShipData{ - Data: model.DataType{ - Header: model.HeaderType{ - ProtocolId: model.ShipProtocolId, - }, - Payload: json.RawMessage([]byte(payloadPlaceholder)), - }, - } - - msg, err := json.Marshal(shipMessage) - if err != nil { - return nil, err - } - - eebusMsg, err := shipUtil.JsonIntoEEBUSJson(msg) - if err != nil { - return nil, err - } - - eebusMsg = strings.ReplaceAll(eebusMsg, `[`+payloadPlaceholder+`]`, string(payload)) - - return []byte(eebusMsg), nil -} - -func (c *ShipConnectionImpl) sendSpineData(data []byte) error { - eebusMsg, err := c.transformSpineDataIntoShipJson(data) - if err != nil { - return err - } - - if isClosed, err := c.dataHandler.IsDataConnectionClosed(); isClosed { - c.CloseConnection(false, 0, "") - return err - } - - // Wrap the message into a binary message with the ship header - shipMsg := []byte{model.MsgTypeData} - shipMsg = append(shipMsg, eebusMsg...) - - err = c.dataHandler.WriteMessageToDataConnection(shipMsg) - if err != nil { - logging.Log().Debug("error sending message: ", err) - return err - } - - return nil -} - -// send a json message for a provided model to the websocket connection -func (c *ShipConnectionImpl) sendShipModel(typ byte, model interface{}) error { - shipMsg, err := c.shipMessage(typ, model) - if err != nil { - return err - } - - err = c.dataHandler.WriteMessageToDataConnection(shipMsg) - if err != nil { - return err - } - - return nil -} - -// Process a SHIP Json message -func (c *ShipConnectionImpl) processShipJsonMessage(message []byte, target any) error { - _, data := c.parseMessage(message, true) - - return json.Unmarshal(data, &target) -} - -// transform a SHIP model into EEBUS specific JSON -func (c *ShipConnectionImpl) shipMessage(typ byte, model interface{}) ([]byte, error) { - if isClosed, err := c.dataHandler.IsDataConnectionClosed(); isClosed { - c.CloseConnection(false, 0, "") - return nil, err - } - - if model == nil { - return nil, errors.New("invalid data") - } - - msg, err := json.Marshal(model) - if err != nil { - return nil, err - } - - eebusMsg, err := shipUtil.JsonIntoEEBUSJson(msg) - if err != nil { - return nil, err - } - - // Wrap the message into a binary message with the ship header - shipMsg := []byte{typ} - shipMsg = append(shipMsg, eebusMsg...) - - return shipMsg, nil -} - -// return the SHIP message type, the SHIP message and an error -// -// enable jsonFormat if the return message is expected to be encoded in the eebus json format -func (c *ShipConnectionImpl) parseMessage(msg []byte, jsonFormat bool) (byte, []byte) { - if len(msg) == 0 { - return 0, nil - } - - // Extract the SHIP header byte - shipHeaderByte := msg[0] - // remove the SHIP header byte from the message - msg = msg[1:] - - if jsonFormat { - return shipHeaderByte, shipUtil.JsonFromEEBUSJson(msg) - } - - return shipHeaderByte, msg -} diff --git a/ship/connection_test.go b/ship/connection_test.go deleted file mode 100644 index 8ef3bb63..00000000 --- a/ship/connection_test.go +++ /dev/null @@ -1,222 +0,0 @@ -package ship - -import ( - "encoding/json" - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "go.uber.org/mock/gomock" -) - -func TestConnectionSuite(t *testing.T) { - suite.Run(t, new(ConnectionSuite)) -} - -type ConnectionSuite struct { - suite.Suite - - sut *ShipConnectionImpl - - shipDataProvider *MockShipServiceDataProvider - shipDataConn *MockWebsocketDataConnection - - spineDataProcessing *MockSpineDataProcessing - - sentMessage []byte -} - -func (s *ConnectionSuite) BeforeTest(suiteName, testName string) { - s.sentMessage = nil - - ctrl := gomock.NewController(s.T()) - - s.shipDataProvider = NewMockShipServiceDataProvider(ctrl) - s.shipDataProvider.EXPECT().HandleShipHandshakeStateUpdate(gomock.Any(), gomock.Any()).AnyTimes() - s.shipDataProvider.EXPECT().HandleConnectionClosed(gomock.Any(), gomock.Any()).AnyTimes() - s.shipDataProvider.EXPECT().IsRemoteServiceForSKIPaired(gomock.Any()).AnyTimes() - s.shipDataProvider.EXPECT().AllowWaitingForTrust(gomock.Any()).AnyTimes() - - s.shipDataConn = NewMockWebsocketDataConnection(ctrl) - s.shipDataConn.EXPECT().InitDataProcessing(gomock.Any()).AnyTimes() - s.shipDataConn.EXPECT().WriteMessageToDataConnection(gomock.Any()).DoAndReturn(func(message []byte) error { s.sentMessage = message; return nil }).AnyTimes() - s.shipDataConn.EXPECT().IsDataConnectionClosed().DoAndReturn(func() (bool, error) { return false, nil }).AnyTimes() - s.shipDataConn.EXPECT().CloseDataConnection(gomock.Any(), gomock.Any()).AnyTimes() - - s.spineDataProcessing = NewMockSpineDataProcessing(ctrl) - s.spineDataProcessing.EXPECT().HandleIncomingSpineMesssage(gomock.Any()).AnyTimes() - - s.sut = NewConnectionHandler(s.shipDataProvider, s.shipDataConn, ShipRoleServer, "LocalShipID", "RemoveDevice", "RemoteShipID") -} - -func (s *ConnectionSuite) Test_RemoteSKI() { - remoteSki := s.sut.RemoteSKI() - assert.NotEqual(s.T(), "", remoteSki) -} - -func (s *ConnectionSuite) Test_DataHandler() { - handler := s.sut.DataHandler() - assert.NotNil(s.T(), handler) -} - -func (s *ConnectionSuite) TestRun() { - s.sut.Run() - state, err := s.sut.ShipHandshakeState() - assert.Nil(s.T(), err) - assert.Equal(s.T(), CmiStateServerWait, state) -} - -func (s *ConnectionSuite) TestShipHandshakeState() { - state, err := s.sut.ShipHandshakeState() - assert.Nil(s.T(), err) - assert.Equal(s.T(), CmiStateInitStart, state) -} - -func (s *ConnectionSuite) TestApprovePendingHandshake() { - s.sut.smeState = CmiStateInitStart - s.sut.ApprovePendingHandshake() - assert.Equal(s.T(), CmiStateInitStart, s.sut.smeState) - - s.sut.smeState = SmeHelloStatePendingListen - s.sut.ApprovePendingHandshake() - assert.Equal(s.T(), SmeProtHStateServerListenProposal, s.sut.smeState) -} - -func (s *ConnectionSuite) TestAbortPendingHandshake() { - s.sut.smeState = CmiStateInitStart - s.sut.AbortPendingHandshake() - assert.Equal(s.T(), CmiStateInitStart, s.sut.smeState) - - s.sut.smeState = SmeHelloStatePendingListen - s.sut.AbortPendingHandshake() - assert.Equal(s.T(), SmeHelloStateAbortDone, s.sut.smeState) -} - -func (s *ConnectionSuite) TestCloseConnection_StateComplete() { - s.sut.smeState = SmeStateComplete - s.sut.CloseConnection(true, 450, "User Close") - state, err := s.sut.ShipHandshakeState() - assert.Nil(s.T(), err) - assert.Equal(s.T(), SmeStateComplete, state) -} - -func (s *ConnectionSuite) TestCloseConnection_StateComplete_2() { - s.sut.smeState = SmeStateError - s.sut.CloseConnection(false, 0, "User Close") - state, err := s.sut.ShipHandshakeState() - assert.Nil(s.T(), err) - assert.Equal(s.T(), SmeStateError, state) -} - -func (s *ConnectionSuite) TestCloseConnection_StateComplete_3() { - s.sut.smeState = SmeStateError - s.sut.CloseConnection(false, 450, "User Close") - state, err := s.sut.ShipHandshakeState() - assert.Nil(s.T(), err) - assert.Equal(s.T(), SmeStateError, state) -} - -func (s *ConnectionSuite) TestShipModelFromMessage() { - msg := []byte{} - data, err := s.sut.shipModelFromMessage(msg) - assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) - - modelData := model.ShipData{} - jsonData, err := json.Marshal(modelData) - assert.Nil(s.T(), err) - - msg = []byte{0} - msg = append(msg, jsonData...) - data, err = s.sut.shipModelFromMessage(msg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), data) -} - -func (s *ConnectionSuite) TestHandleIncomingShipMessage() { - modelData := model.ShipData{} - jsonData, err := json.Marshal(modelData) - assert.Nil(s.T(), err) - - msg := []byte{0} - msg = append(msg, jsonData...) - - s.sut.HandleIncomingShipMessage(msg) - - spineData := `{"datagram":{}}` - jsonData = []byte(spineData) - - modelData = model.ShipData{ - Data: model.DataType{ - Payload: jsonData, - }, - } - jsonData, err = json.Marshal(modelData) - assert.Nil(s.T(), err) - - msg = []byte{0} - msg = append(msg, jsonData...) - - s.sut.HandleIncomingShipMessage(msg) - - s.sut.spineDataProcessing = s.spineDataProcessing - - s.sut.HandleIncomingShipMessage(msg) -} - -func (s *ConnectionSuite) TestReportConnectionError() { - s.sut.ReportConnectionError(nil) - assert.Equal(s.T(), SmeStateError, s.sut.smeState) - - s.sut.smeState = SmeHelloStateReadyListen - s.sut.ReportConnectionError(nil) - assert.Equal(s.T(), SmeHelloStateRejected, s.sut.smeState) - - s.sut.smeState = SmeHelloStateRemoteAbortDone - s.sut.ReportConnectionError(nil) - assert.Equal(s.T(), SmeHelloStateRemoteAbortDone, s.sut.smeState) - - s.sut.smeState = SmeHelloStateAbort - s.sut.ReportConnectionError(nil) - assert.Equal(s.T(), SmeHelloStateAbort, s.sut.smeState) -} - -func (s *ConnectionSuite) TestSendShipModel() { - err := s.sut.sendShipModel(model.MsgTypeInit, nil) - assert.NotNil(s.T(), err) - - closeMessage := model.ConnectionClose{ - ConnectionClose: model.ConnectionCloseType{ - Phase: model.ConnectionClosePhaseTypeAnnounce, - }, - } - - err = s.sut.sendShipModel(model.MsgTypeControl, closeMessage) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), s.sentMessage) -} - -func (s *ConnectionSuite) TestProcessShipJsonMessage() { - closeMessage := model.ConnectionClose{ - ConnectionClose: model.ConnectionCloseType{ - Phase: model.ConnectionClosePhaseTypeAnnounce, - }, - } - msg, err := json.Marshal(closeMessage) - assert.Nil(s.T(), err) - - newMsg := []byte{model.MsgTypeControl} - newMsg = append(newMsg, msg...) - - var data any - err = s.sut.processShipJsonMessage(newMsg, &data) - assert.Nil(s.T(), err) -} - -func (s *ConnectionSuite) TestSendSpineMessage() { - data := `{"datagram":{"header":{},"payload":{"cmd":[]}}}` - - err := s.sut.sendSpineData([]byte(data)) - assert.Nil(s.T(), err) -} diff --git a/ship/handshake.go b/ship/handshake.go deleted file mode 100644 index 86b4b0d1..00000000 --- a/ship/handshake.go +++ /dev/null @@ -1,282 +0,0 @@ -package ship - -import ( - "errors" - "time" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/ship/model" -) - -// handle incoming SHIP messages and coordinate Handshake States -func (c *ShipConnectionImpl) handleShipMessage(timeout bool, message []byte) { - if len(message) > 2 { - var closeMsg model.ConnectionClose - err := c.processShipJsonMessage(message, &closeMsg) - if err == nil && closeMsg.ConnectionClose.Phase != "" { - switch closeMsg.ConnectionClose.Phase { - case model.ConnectionClosePhaseTypeAnnounce: - // SHIP 13.4.7: Connection Termination Confirm - closeMessage := model.ConnectionClose{ - ConnectionClose: model.ConnectionCloseType{ - Phase: model.ConnectionClosePhaseTypeConfirm, - }, - } - - _ = c.sendShipModel(model.MsgTypeEnd, closeMessage) - - // wait a bit to let it send - <-time.After(500 * time.Millisecond) - - // - c.dataHandler.CloseDataConnection(4001, "close") - c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeStateComplete) - case model.ConnectionClosePhaseTypeConfirm: - // we got a confirmation so close this connection - c.dataHandler.CloseDataConnection(4001, "close") - c.serviceDataProvider.HandleConnectionClosed(c, c.getState() == SmeStateComplete) - } - - return - } - } - - c.handleState(timeout, message) -} - -// set a new handshake state and handle timers if needed -func (c *ShipConnectionImpl) setState(newState ShipMessageExchangeState, err error) { - c.mux.Lock() - - oldState := c.smeState - - c.smeState = newState - - switch newState { - case SmeHelloStateReadyInit: - c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) - case SmeHelloStatePendingInit: - c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) - case SmeHelloStateOk: - c.stopHandshakeTimer() - case SmeHelloStateAbort, SmeHelloStateAbortDone, SmeHelloStateRemoteAbortDone, SmeHelloStateRejected: - c.stopHandshakeTimer() - case SmeProtHStateClientListenChoice: - c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) - case SmeProtHStateClientOk: - c.stopHandshakeTimer() - } - - c.smeError = nil - if oldState != newState { - c.smeError = err - state := ShipState{ - State: newState, - Error: err, - } - c.mux.Unlock() - c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.remoteSKI, state) - return - } - c.mux.Unlock() -} - -func (c *ShipConnectionImpl) getState() ShipMessageExchangeState { - c.mux.Lock() - defer c.mux.Unlock() - - return c.smeState -} - -// handle handshake state transitions -func (c *ShipConnectionImpl) handleState(timeout bool, message []byte) { - switch c.getState() { - case SmeStateError: - logging.Log().Debug(c.RemoteSKI, "connection is in error state") - return - - // cmiStateInit - case CmiStateInitStart: - // triggered without a message received - c.handshakeInit_cmiStateInitStart() - - case CmiStateClientWait: - if timeout { - c.endHandshakeWithError(errors.New("ship client handshake timeout")) - return - } - - c.handshakeInit_cmiStateClientWait(message) - - case CmiStateServerWait: - if timeout { - c.endHandshakeWithError(errors.New("ship server handshake timeout")) - return - } - c.handshakeInit_cmiStateServerWait(message) - - // smeHello - - case SmeHelloState: - // check if the service is already trusted or the role is client, - // which means it was initiated from this service usually by triggering the - // pairing service - // go to substate ready if so, otherwise to substate pending - - if c.serviceDataProvider.IsRemoteServiceForSKIPaired(c.remoteSKI) || c.role == ShipRoleClient { - c.setState(SmeHelloStateReadyInit, nil) - } else { - c.setState(SmeHelloStatePendingInit, nil) - } - c.handleState(timeout, message) - - case SmeHelloStateReadyInit: - c.handshakeHello_Init() - - case SmeHelloStateReadyListen: - c.handshakeHello_ReadyListen(timeout, message) - - case SmeHelloStatePendingInit: - c.handshakeHello_PendingInit() - - case SmeHelloStatePendingListen: - c.handshakeHello_PendingListen(timeout, message) - - case SmeHelloStateOk: - c.handshakeProtocol_Init() - - case SmeHelloStateAbort: - c.handshakeHello_Abort() - - case SmeHelloStateAbortDone, SmeHelloStateRemoteAbortDone: - go func() { - time.Sleep(time.Second) - c.CloseConnection(false, 4452, "Node rejected by application") - }() - - // smeProtocol - - case SmeProtHStateServerListenProposal: - c.handshakeProtocol_smeProtHStateServerListenProposal(message) - - case SmeProtHStateServerListenConfirm: - c.handshakeProtocol_smeProtHStateServerListenConfirm(message) - - case SmeProtHStateClientListenChoice: - c.stopHandshakeTimer() - c.handshakeProtocol_smeProtHStateClientListenChoice(message) - - case SmeProtHStateClientOk: - c.setAndHandleState(SmePinStateCheckInit) - - case SmeProtHStateServerOk: - c.setAndHandleState(SmePinStateCheckInit) - - // smePinState - - case SmePinStateCheckInit: - c.handshakePin_Init() - - case SmePinStateCheckListen: - c.handshakePin_smePinStateCheckListen(message) - - case SmePinStateCheckOk: - c.handshakeAccessMethods_Init() - - // smeAccessMethods - - case SmeAccessMethodsRequest: - c.handshakeAccessMethods_Request(message) - } -} - -// set a state and trigger handling it -func (c *ShipConnectionImpl) setAndHandleState(state ShipMessageExchangeState) { - c.setState(state, nil) - c.handleState(false, nil) -} - -// SHIP handshake is approved, now set the new state and the SPINE read handler -func (c *ShipConnectionImpl) approveHandshake() { - // Report to SPINE local device about this remote device connection - c.spineDataProcessing = c.serviceDataProvider.SetupRemoteDevice(c.remoteSKI, c) - c.stopHandshakeTimer() - c.setState(SmeStateComplete, nil) -} - -// end the handshake process because of an error -func (c *ShipConnectionImpl) endHandshakeWithError(err error) { - c.stopHandshakeTimer() - - c.setState(SmeStateError, err) - - logging.Log().Debug(c.RemoteSKI, "SHIP handshake error:", err) - - c.CloseConnection(true, 0, err.Error()) - - state := ShipState{ - State: SmeStateError, - Error: err, - } - c.serviceDataProvider.HandleShipHandshakeStateUpdate(c.remoteSKI, state) -} - -// set the handshake timer to a new duration and start the channel -func (c *ShipConnectionImpl) setHandshakeTimer(timerType timeoutTimerType, duration time.Duration) { - c.stopHandshakeTimer() - - c.setHandshakeTimerRunning(true) - c.setHandshakeTimerType(timerType) - - go func() { - select { - case <-c.handshakeTimerStopChan: - return - case <-time.After(duration): - c.setHandshakeTimerRunning(false) - c.handleState(true, nil) - return - } - }() -} - -// stop the handshake timer and close the channel -func (c *ShipConnectionImpl) stopHandshakeTimer() { - if !c.getHandshakeTimerRunnging() { - return - } - - select { - case c.handshakeTimerStopChan <- struct{}{}: - default: - } - c.setHandshakeTimerRunning(false) -} - -func (c *ShipConnectionImpl) setHandshakeTimerRunning(value bool) { - c.handshakeTimerMux.Lock() - defer c.handshakeTimerMux.Unlock() - - c.handshakeTimerRunning = value -} - -func (c *ShipConnectionImpl) getHandshakeTimerRunnging() bool { - c.handshakeTimerMux.Lock() - defer c.handshakeTimerMux.Unlock() - - return c.handshakeTimerRunning -} - -func (c *ShipConnectionImpl) setHandshakeTimerType(timerType timeoutTimerType) { - c.handshakeTimerMux.Lock() - defer c.handshakeTimerMux.Unlock() - - c.handshakeTimerType = timerType -} - -func (c *ShipConnectionImpl) getHandshakeTimerType() timeoutTimerType { - c.handshakeTimerMux.Lock() - defer c.handshakeTimerMux.Unlock() - - return c.handshakeTimerType -} diff --git a/ship/hs_access.go b/ship/hs_access.go deleted file mode 100644 index 65814b16..00000000 --- a/ship/hs_access.go +++ /dev/null @@ -1,81 +0,0 @@ -package ship - -import ( - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/enbility/eebus-go/ship/model" -) - -// Handshake Access covers the states smeAccess... - -func (c *ShipConnectionImpl) handshakeAccessMethods_Init() { - // Access Methods - accessMethodsRequest := model.AccessMethodsRequest{ - AccessMethodsRequest: model.AccessMethodsRequestType{}, - } - - if err := c.sendShipModel(model.MsgTypeControl, accessMethodsRequest); err != nil { - c.endHandshakeWithError(err) - return - } - - c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) - c.setState(SmeAccessMethodsRequest, nil) -} - -func (c *ShipConnectionImpl) handshakeAccessMethods_Request(message []byte) { - _, data := c.parseMessage(message, true) - - dataString := string(data) - - if strings.Contains(dataString, "\"accessMethodsRequest\":{") { - methodsId := c.localShipID - - accessMethods := model.AccessMethods{ - AccessMethods: model.AccessMethodsType{ - Id: &methodsId, - }, - } - if err := c.sendShipModel(model.MsgTypeControl, accessMethods); err != nil { - c.endHandshakeWithError(err) - } - return - } else if strings.Contains(dataString, "\"accessMethods\":{") { - // compare SHIP ID to stored value on pairing. SKI + SHIP ID should be verified on connection - // otherwise close connection with error "close 4450: SHIP id mismatch" - - var accessMethods model.AccessMethods - if err := json.Unmarshal([]byte(data), &accessMethods); err != nil { - c.endHandshakeWithError(err) - return - } - - if accessMethods.AccessMethods.Id == nil { - c.endHandshakeWithError(errors.New("Access methods response does not contain SHIP ID")) - return - } - - // if the ID string is empty, then we don't know it yet and can't be verified - if len(c.remoteShipID) > 0 && c.remoteShipID != *accessMethods.AccessMethods.Id { - c.endHandshakeWithError(errors.New("SHIP id mismatch")) - return - } - - // save and report the SHIP ID - if len(c.remoteShipID) == 0 { - c.remoteShipID = *accessMethods.AccessMethods.Id - - c.serviceDataProvider.ReportServiceShipID(c.remoteSKI, c.remoteShipID) - } - - } else { - c.endHandshakeWithError(fmt.Errorf("access methods: invalid response: %s", dataString)) - return - } - - c.setState(SmeStateApproved, nil) - c.approveHandshake() -} diff --git a/ship/hs_access_test.go b/ship/hs_access_test.go deleted file mode 100644 index 53f4f148..00000000 --- a/ship/hs_access_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package ship - -import ( - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestAccessSuite(t *testing.T) { - suite.Run(t, new(AccessSuite)) -} - -type AccessSuite struct { - suite.Suite -} - -func (s *AccessSuite) Test_Init() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmePinStateCheckOk, nil) - sut.handleState(false, nil) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeAccessMethodsRequest, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *AccessSuite) Test_Request() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmeAccessMethodsRequest, nil) - - accessMsg := model.AccessMethodsRequest{ - AccessMethodsRequest: model.AccessMethodsRequestType{}, - } - msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeAccessMethodsRequest, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *AccessSuite) Test_Request_Invalid() { - sut, _ := initTest(ShipRoleClient) - - sut.setState(SmeAccessMethodsRequest, nil) - - accessMsg := model.MessageProtocolHandshake{} - msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeStateError, sut.getState()) - - shutdownTest(sut) -} - -func (s *AccessSuite) Test_Methods_Ok() { - sut, _ := initTest(ShipRoleClient) - - sut.setState(SmeAccessMethodsRequest, nil) - - accessMsg := model.AccessMethods{ - AccessMethods: model.AccessMethodsType{ - Id: util.Ptr("RemoteShipID"), - }, - } - msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeStateComplete, sut.getState()) - - shutdownTest(sut) -} - -func (s *AccessSuite) Test_Methods_NoID() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmeAccessMethodsRequest, nil) - - accessMsg := model.AccessMethods{ - AccessMethods: model.AccessMethodsType{}, - } - msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *AccessSuite) Test_Methods_WrongShipID() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmeAccessMethodsRequest, nil) - - accessMsg := model.AccessMethods{ - AccessMethods: model.AccessMethodsType{ - Id: util.Ptr("WrongRemoteShipID"), - }, - } - msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *AccessSuite) Test_Methods_NoShipID() { - sut, _ := initTest(ShipRoleClient) - - sut.remoteShipID = "" - - sut.setState(SmeAccessMethodsRequest, nil) - - accessMsg := model.AccessMethods{ - AccessMethods: model.AccessMethodsType{ - Id: util.Ptr(""), - }, - } - msg, err := sut.shipMessage(model.MsgTypeControl, accessMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeStateComplete, sut.getState()) - - shutdownTest(sut) -} diff --git a/ship/hs_hello.go b/ship/hs_hello.go deleted file mode 100644 index 6beaf811..00000000 --- a/ship/hs_hello.go +++ /dev/null @@ -1,258 +0,0 @@ -package ship - -import ( - "time" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/ship/model" - "github.com/enbility/eebus-go/util" -) - -// Handshake Hello covers the states smeHello... - -// SME_HELLO_STATE_READY_INIT -func (c *ShipConnectionImpl) handshakeHello_Init() { - if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypeReady, tHelloInit, false); err != nil { - c.setAndHandleState(SmeHelloStateAbort) - return - } - - c.setState(SmeHelloStateReadyListen, nil) -} - -// SME_HELLO_STATE_READY_LISTEN -func (c *ShipConnectionImpl) handshakeHello_ReadyListen(timeout bool, message []byte) { - if timeout { - c.handshakeHello_ReadyTimeout() - return - } - - var helloReturnMsg model.ConnectionHello - if err := c.processShipJsonMessage(message, &helloReturnMsg); err != nil { - c.setAndHandleState(SmeHelloStateAbort) - return - } - - hello := helloReturnMsg.ConnectionHello - - switch hello.Phase { - case model.ConnectionHelloPhaseTypeReady: - // HELLO_OK - c.setState(SmeHelloStateOk, nil) - - case model.ConnectionHelloPhaseTypePending: - // the phase is still pending an no prolongationRequest is set, ignore the message - if hello.ProlongationRequest == nil { - return - } - - // if we got a prolongation request, accept it - if *hello.ProlongationRequest { - if c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { - // re-init timer - c.setHandshakeTimer(timeoutTimerTypeWaitForReady, tHelloInit) - } - - if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypeReady, tHelloInit, false); err != nil { - c.endHandshakeWithError(err) - } - - return - } - - // TODO: what to do if this is false? - - case model.ConnectionHelloPhaseTypeAborted: - c.setAndHandleState(SmeHelloStateRemoteAbortDone) - - return - - default: - // don't accept any other responses - logging.Log().Errorf("Unexpected connection hello phase: %s", hello.Phase) - c.setAndHandleState(SmeHelloStateAbort) - return - } - - c.handleState(false, nil) -} - -func (c *ShipConnectionImpl) handshakeHello_ReadyTimeout() { - c.setAndHandleState(SmeHelloStateAbort) -} - -// SME_HELLO_ABORT -func (c *ShipConnectionImpl) handshakeHello_Abort() { - c.stopHandshakeTimer() - - if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypeAborted, 0, false); err != nil { - c.endHandshakeWithError(err) - return - } - - c.setAndHandleState(SmeHelloStateAbortDone) -} - -// SME_HELLO_PENDING_INIT -func (c *ShipConnectionImpl) handshakeHello_PendingInit() { - if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, tHelloInit, false); err != nil { - c.endHandshakeWithError(err) - return - } - - c.setState(SmeHelloStatePendingListen, nil) - - if !c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { - c.setAndHandleState(SmeHelloStateAbort) - } -} - -// SME_HELLO_PENDING_LISTEN -func (c *ShipConnectionImpl) handshakeHello_PendingListen(timeout bool, message []byte) { - if timeout { - // The device needs to be in a state for the user to allow trusting the device - // e.g. either the web UI or by other means - if !c.serviceDataProvider.AllowWaitingForTrust(c.remoteShipID) { - c.handshakeHello_PendingTimeout() - } else { - c.handshakeHello_PendingProlongationRequest() - } - - return - } - - var helloReturnMsg model.ConnectionHello - if err := c.processShipJsonMessage(message, &helloReturnMsg); err != nil { - c.setAndHandleState(SmeHelloStateAbort) - return - } - - hello := helloReturnMsg.ConnectionHello - - switch hello.Phase { - case model.ConnectionHelloPhaseTypeReady: - if hello.Waiting == nil { - c.setAndHandleState(SmeHelloStateAbort) - return - } - - c.stopHandshakeTimer() - - newDuration := time.Duration(*hello.Waiting) * time.Millisecond - duration := tHelloProlongThrInc - if newDuration >= duration { - // the duration has to be reduced - duration = newDuration - duration - - // check if it is less than T_hello_prolong_min - if newDuration >= tHelloProlongMin { - c.setHandshakeTimer(timeoutTimerTypeSendProlongationRequest, duration) - return - } - } - - if newDuration < tHelloProlongMin { - // I interpret 13.4.4.1.3 Page 64 Line 1550-1553 as this resulting in a timeout state - // TODO: verify this - c.setAndHandleState(SmeHelloStateAbort) - } - - case model.ConnectionHelloPhaseTypePending: - if hello.Waiting != nil && hello.ProlongationRequest == nil { - c.stopHandshakeTimer() - - newDuration := time.Duration(*hello.Waiting) * time.Millisecond - c.lastReceivedWaitingValue = newDuration - duration := tHelloProlongThrInc - if newDuration >= duration { - // the duration has to be reduced - duration = newDuration - duration - - // check if it is less than T_hello_prolong_min - if newDuration >= tHelloProlongMin { - c.setHandshakeTimer(timeoutTimerTypeSendProlongationRequest, duration) - return - } - } - - if newDuration < tHelloProlongMin { - // I interpret 13.4.4.1.3 Page 64 Line 1557-1560 as this resulting in a timeout state - // TODO: verify this - c.setAndHandleState(SmeHelloStateAbort) - } - - return - } - - if hello.Waiting == nil && hello.ProlongationRequest != nil && *hello.ProlongationRequest { - // if we got a prolongation request, accept it - if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, tHelloInit, false); err != nil { - c.endHandshakeWithError(err) - } - - return - } - - c.setAndHandleState(SmeHelloStateAbort) - - case model.ConnectionHelloPhaseTypeAborted: - c.setAndHandleState(SmeHelloStateRemoteAbortDone) - return - - default: - // don't accept any other responses - logging.Log().Errorf("Unexpected connection hello phase: %s", hello.Phase) - c.setAndHandleState(SmeHelloStateAbort) - return - } - - c.handleState(false, nil) -} - -func (c *ShipConnectionImpl) handshakeHello_PendingProlongationRequest() { - if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, 0, true); err != nil { - c.endHandshakeWithError(err) - return - } - - // TODO: we need to set the timer to the last received waiting value - c.setHandshakeTimer(timeoutTimerTypeProlongRequestReply, tHelloInit) -} - -func (c *ShipConnectionImpl) handshakeHello_PendingTimeout() { - if c.getHandshakeTimerType() != timeoutTimerTypeSendProlongationRequest { - c.setAndHandleState(SmeHelloStateAbort) - return - } - - if err := c.handshakeHelloSend(model.ConnectionHelloPhaseTypePending, 0, true); err != nil { - c.endHandshakeWithError(err) - return - } - - if c.lastReceivedWaitingValue == 0 { - newValue := float64(tHelloInit.Milliseconds()) * 1.1 - c.lastReceivedWaitingValue = time.Duration(newValue) - } - c.setHandshakeTimer(timeoutTimerTypeProlongRequestReply, c.lastReceivedWaitingValue) -} - -func (c *ShipConnectionImpl) handshakeHelloSend(phase model.ConnectionHelloPhaseType, waitingDuration time.Duration, prolongation bool) error { - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: phase, - }, - } - - if waitingDuration > 0 { - helloMsg.ConnectionHello.Waiting = util.Ptr(uint(waitingDuration.Milliseconds())) - } - if prolongation { - helloMsg.ConnectionHello.ProlongationRequest = &prolongation - } - - if err := c.sendShipModel(model.MsgTypeControl, helloMsg); err != nil { - return err - } - return nil -} diff --git a/ship/hs_hello_client_test.go b/ship/hs_hello_client_test.go deleted file mode 100644 index a5158d07..00000000 --- a/ship/hs_hello_client_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package ship - -import ( - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestHelloClientSuite(t *testing.T) { - suite.Run(t, new(HelloClientSuite)) -} - -// Hello Client role specific tests -type HelloClientSuite struct { - suite.Suite - role shipRole -} - -func (s *HelloClientSuite) BeforeTest(suiteName, testName string) { - s.role = ShipRoleClient -} - -func (s *HelloClientSuite) Test_InitialState() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloState, nil) - sut.handleState(false, nil) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloClientSuite) Test_ReadyListen_Ok() { - sut, _ := initTest(s.role) - - sut.setState(SmeHelloStateReadyInit, nil) // inits the timer - sut.setState(SmeHelloStateReadyListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypeReady, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - // the state goes from smeHelloStateOk directly to smeProtHStateClientInit to smeProtHStateClientListenChoice - assert.Equal(s.T(), SmeProtHStateClientListenChoice, sut.getState()) - - shutdownTest(sut) -} diff --git a/ship/hs_hello_test.go b/ship/hs_hello_test.go deleted file mode 100644 index 40aa874d..00000000 --- a/ship/hs_hello_test.go +++ /dev/null @@ -1,350 +0,0 @@ -package ship - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/ship/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestHelloSuite(t *testing.T) { - suite.Run(t, new(HelloSuite)) -} - -type HelloSuite struct { - suite.Suite - role shipRole -} - -func (s *HelloSuite) BeforeTest(suiteName, testName string) { - s.role = ShipRoleServer -} - -func (s *HelloSuite) Test_InitialState() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloState, nil) - sut.handleState(false, nil) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_ReadyListen_Init() { - sut, _ := initTest(s.role) - - sut.setState(SmeHelloStateReadyInit, nil) - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_ReadyListen_Ok() { - sut, _ := initTest(s.role) - - sut.setState(SmeHelloStateReadyInit, nil) // inits the timer - sut.setState(SmeHelloStateReadyListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypeReady, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - // the state goes from smeHelloStateOk directly to smeProtHStateServerInit to smeProtHStateClientListenProposal - assert.Equal(s.T(), SmeProtHStateServerListenProposal, sut.getState()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_ReadyListen_Timeout() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStateReadyInit, nil) // inits the timer - sut.setState(SmeHelloStateReadyListen, nil) - - if !util.IsRunningOnCI() { - // test if the function is triggered correctly via the timer - time.Sleep(tHelloInit + time.Second) - } else { - // speed up the test by running the method directly - sut.handshakeHello_ReadyListen(true, nil) - } - - assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_ReadyListen_Ignore() { - sut, _ := initTest(s.role) - - sut.setState(SmeHelloStateReadyInit, nil) // inits the timer - sut.setState(SmeHelloStateReadyListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypePending, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_ReadyListen_Prolongation() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStateReadyInit, nil) // inits the timer - sut.setState(SmeHelloStateReadyListen, nil) - - data.allowWaitingForTrust = true - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypePending, - ProlongationRequest: util.Ptr(true), - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_ReadyListen_Abort() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStateReadyInit, nil) // inits the timer - sut.setState(SmeHelloStateReadyListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypeAborted, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleShipMessage(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateRemoteAbortDone, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingInit() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStatePendingInit, nil) - sut.handleState(false, nil) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingListen() { - sut, _ := initTest(s.role) - - sut.setState(SmeHelloStatePendingInit, nil) // inits the timer - sut.setState(SmeHelloStatePendingListen, nil) - sut.handleState(false, nil) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingListen_Timeout() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStatePendingInit, nil) // inits the timer - sut.setState(SmeHelloStatePendingListen, nil) - - if !util.IsRunningOnCI() { - // test if the function is triggered correctly via the timer - time.Sleep(tHelloInit + time.Second) - } else { - // speed up the test by running the method directly - sut.handshakeHello_PendingListen(true, nil) - } - - assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingListen_Timeout_Prolongation() { - sut, data := initTest(s.role) - - data.allowWaitingForTrust = true - - sut.setState(SmeHelloStatePendingInit, nil) // inits the timer - sut.setState(SmeHelloStatePendingListen, nil) - - // speed up the test by running the method directly, the timer is already checked - sut.handshakeHello_PendingListen(true, nil) - - assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingListen_ReadyAbort() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStatePendingInit, nil) // inits the timer - sut.setState(SmeHelloStatePendingListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypeReady, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleShipMessage(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateAbortDone, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingListen_ReadyWaiting() { - sut, _ := initTest(s.role) - - sut.setState(SmeHelloStatePendingInit, nil) // inits the timer - sut.setState(SmeHelloStatePendingListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypeReady, - Waiting: util.Ptr(uint(tHelloInit.Milliseconds())), - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleShipMessage(false, msg) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingListen_Abort() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStatePendingInit, nil) // inits the timer - sut.setState(SmeHelloStatePendingListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypeAborted, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleShipMessage(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStateRemoteAbortDone, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingListen_PendingWaiting() { - sut, _ := initTest(s.role) - - sut.setState(SmeHelloStatePendingInit, nil) // inits the timer - sut.setState(SmeHelloStatePendingListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypePending, - Waiting: util.Ptr(uint(tHelloInit.Milliseconds())), - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleShipMessage(false, msg) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) - - shutdownTest(sut) -} - -func (s *HelloSuite) Test_PendingListen_PendingProlongation() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStatePendingInit, nil) // inits the timer - sut.setState(SmeHelloStatePendingListen, nil) - - helloMsg := model.ConnectionHello{ - ConnectionHello: model.ConnectionHelloType{ - Phase: model.ConnectionHelloPhaseTypePending, - ProlongationRequest: util.Ptr(true), - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, helloMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleShipMessage(false, msg) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeHelloStatePendingListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} diff --git a/ship/hs_helper_test.go b/ship/hs_helper_test.go deleted file mode 100644 index e72ca97a..00000000 --- a/ship/hs_helper_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package ship - -import ( - "sync" -) - -type dataHandlerTest struct { - sentMessage []byte - - mux sync.Mutex - - allowWaitingForTrust bool - - handleConnectionClosedInvoked bool -} - -func (s *dataHandlerTest) lastMessage() []byte { - s.mux.Lock() - defer s.mux.Unlock() - - return s.sentMessage -} - -var _ WebsocketDataConnection = (*dataHandlerTest)(nil) - -func (s *dataHandlerTest) InitDataProcessing(dataProcessing WebsocketDataProcessing) {} - -func (s *dataHandlerTest) WriteMessageToDataConnection(message []byte) error { - s.mux.Lock() - defer s.mux.Unlock() - - s.sentMessage = message - - return nil -} - -func (s *dataHandlerTest) CloseDataConnection(int, string) {} -func (w *dataHandlerTest) IsDataConnectionClosed() (bool, error) { return false, nil } -func (w *dataHandlerTest) SetupRemoteDevice(ski string, writeI SpineDataConnection) SpineDataProcessing { - return nil -} - -var _ ShipServiceDataProvider = (*dataHandlerTest)(nil) - -func (s *dataHandlerTest) IsRemoteServiceForSKIPaired(string) bool { return true } -func (s *dataHandlerTest) HandleConnectionClosed(ShipConnection, bool) { - s.handleConnectionClosedInvoked = true -} -func (s *dataHandlerTest) ReportServiceShipID(string, string) {} -func (s *dataHandlerTest) AllowWaitingForTrust(string) bool { - return s.allowWaitingForTrust -} -func (s *dataHandlerTest) HandleShipHandshakeStateUpdate(string, ShipState) {} - -func initTest(role shipRole) (*ShipConnectionImpl, *dataHandlerTest) { - dataHandler := &dataHandlerTest{} - conhandler := NewConnectionHandler(dataHandler, dataHandler, role, "LocalShipID", "RemoveDevice", "RemoteShipID") - - return conhandler, dataHandler -} - -func shutdownTest(conhandler *ShipConnectionImpl) { - conhandler.stopHandshakeTimer() -} diff --git a/ship/hs_init.go b/ship/hs_init.go deleted file mode 100644 index 5060e4da..00000000 --- a/ship/hs_init.go +++ /dev/null @@ -1,72 +0,0 @@ -package ship - -import ( - "fmt" - - "github.com/enbility/eebus-go/ship/model" -) - -// Handshake initialization covers the states cmiState... - -// CMI_STATE_INIT_START -func (c *ShipConnectionImpl) handshakeInit_cmiStateInitStart() { - switch c.role { - case ShipRoleClient: - // CMI_STATE_CLIENT_SEND - c.setState(CmiStateClientSend, nil) - if err := c.dataHandler.WriteMessageToDataConnection(shipInit); err != nil { - c.endHandshakeWithError(err) - return - } - c.setState(CmiStateClientWait, nil) - case ShipRoleServer: - c.setState(CmiStateServerWait, nil) - } - - c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) -} - -// CMI_STATE_SERVER_WAIT -func (c *ShipConnectionImpl) handshakeInit_cmiStateServerWait(message []byte) { - c.setState(CmiStateServerEvaluate, nil) - - if !c.handshakeInit_cmiStateEvaluate(message) { - return - } - - if err := c.dataHandler.WriteMessageToDataConnection(shipInit); err != nil { - c.endHandshakeWithError(err) - return - } - - c.setAndHandleState(SmeHelloState) -} - -// CMI_STATE_CLIENT_WAIT -func (c *ShipConnectionImpl) handshakeInit_cmiStateClientWait(message []byte) { - c.setState(CmiStateClientEvaluate, nil) - - if !c.handshakeInit_cmiStateEvaluate(message) { - return - } - - c.setAndHandleState(SmeHelloState) -} - -// CMI_STATE_SERVER_EVALUATE -// CMI_STATE_CLIENT_EVALUATE -// returns false in case of an error -func (c *ShipConnectionImpl) handshakeInit_cmiStateEvaluate(message []byte) bool { - msgType, data := c.parseMessage(message, false) - - if msgType != model.MsgTypeInit { - c.endHandshakeWithError(fmt.Errorf("Invalid SHIP MessageType, expected 0 and got %s" + string(msgType))) - return false - } - if len(data) > 0 && data[0] != byte(0) { - c.endHandshakeWithError(fmt.Errorf("Invalid SHIP MessageValue, expected 0 and got %s" + string(data))) - return false - } - - return true -} diff --git a/ship/hs_init_client_test.go b/ship/hs_init_client_test.go deleted file mode 100644 index 2e095b7a..00000000 --- a/ship/hs_init_client_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package ship - -import ( - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestInitClientSuite(t *testing.T) { - suite.Run(t, new(InitClientSuite)) -} - -type InitClientSuite struct { - suite.Suite - role shipRole -} - -func (s *InitClientSuite) BeforeTest(suiteName, testName string) { - s.role = ShipRoleClient -} - -func (s *InitClientSuite) Test_Init() { - sut, _ := initTest(s.role) - - assert.Equal(s.T(), CmiStateInitStart, sut.getState()) - - shutdownTest(sut) -} - -func (s *InitClientSuite) Test_Start() { - sut, data := initTest(s.role) - - sut.setState(CmiStateInitStart, nil) - - sut.handleState(false, nil) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), CmiStateClientWait, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - assert.Equal(s.T(), shipInit, data.lastMessage()) - - shutdownTest(sut) -} - -func (s *InitClientSuite) Test_ClientWait() { - sut, data := initTest(s.role) - - sut.setState(CmiStateClientWait, nil) - - sut.handleState(false, shipInit) - - // the state goes from smeHelloState directly to smeHelloStateReadyInit to smeHelloStateReadyListen - assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *InitClientSuite) Test_ClientWait_Timeout() { - sut, data := initTest(s.role) - - sut.setState(CmiStateClientWait, nil) - - sut.handleState(true, nil) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - assert.Equal(s.T(), data.handleConnectionClosedInvoked, true) - - shutdownTest(sut) -} - -func (s *InitClientSuite) Test_ClientWait_InvalidMsgType() { - sut, data := initTest(s.role) - - sut.setState(CmiStateClientWait, nil) - - sut.handleState(false, []byte{0x05, 0x00}) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *InitClientSuite) Test_ClientWait_InvalidData() { - sut, data := initTest(s.role) - - sut.setState(CmiStateClientWait, nil) - - sut.handleState(false, []byte{model.MsgTypeInit, 0x05}) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} diff --git a/ship/hs_init_server_test.go b/ship/hs_init_server_test.go deleted file mode 100644 index a7125455..00000000 --- a/ship/hs_init_server_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package ship - -import ( - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestInitServerSuite(t *testing.T) { - suite.Run(t, new(InitServerSuite)) -} - -type InitServerSuite struct { - suite.Suite - role shipRole -} - -func (s *InitServerSuite) BeforeTest(suiteName, testName string) { - s.role = ShipRoleServer -} - -func (s *InitServerSuite) Test_Init() { - sut, _ := initTest(s.role) - - assert.Equal(s.T(), CmiStateInitStart, sut.getState()) - - shutdownTest(sut) -} - -func (s *InitServerSuite) Test_Start() { - sut, _ := initTest(s.role) - - sut.setState(CmiStateInitStart, nil) - - sut.handleState(false, nil) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), CmiStateServerWait, sut.getState()) - - shutdownTest(sut) -} - -func (s *InitServerSuite) Test_ServerWait() { - sut, data := initTest(s.role) - - sut.setState(CmiStateServerWait, nil) - - sut.handleState(false, shipInit) - - // the state goes from smeHelloState directly to smeHelloStateReadyInit to smeHelloStateReadyListen - assert.Equal(s.T(), SmeHelloStateReadyListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *InitServerSuite) Test_ServerWait_InvalidMsgType() { - sut, data := initTest(s.role) - - sut.setState(CmiStateServerWait, nil) - - sut.handleState(false, []byte{0x05, 0x00}) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *InitServerSuite) Test_ServerWait_InvalidData() { - sut, data := initTest(s.role) - - sut.setState(CmiStateServerWait, nil) - - sut.handleState(false, []byte{model.MsgTypeInit, 0x05}) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} diff --git a/ship/hs_pin.go b/ship/hs_pin.go deleted file mode 100644 index 8a9c91d3..00000000 --- a/ship/hs_pin.go +++ /dev/null @@ -1,50 +0,0 @@ -package ship - -import ( - "encoding/json" - "errors" - - "github.com/enbility/eebus-go/ship/model" -) - -// Handshake Pin covers the states smePin... - -func (c *ShipConnectionImpl) handshakePin_Init() { - c.setState(SmePinStateCheckInit, nil) - - pinState := model.ConnectionPinState{ - ConnectionPinState: model.ConnectionPinStateType{ - PinState: model.PinStateTypeNone, - }, - } - - if err := c.sendShipModel(model.MsgTypeControl, pinState); err != nil { - c.endHandshakeWithError(err) - return - } - - c.setState(SmePinStateCheckListen, nil) -} - -func (c *ShipConnectionImpl) handshakePin_smePinStateCheckListen(message []byte) { - _, data := c.parseMessage(message, true) - - var connectionPinState model.ConnectionPinState - if err := json.Unmarshal([]byte(data), &connectionPinState); err != nil { - c.endHandshakeWithError(err) - return - } - - switch connectionPinState.ConnectionPinState.PinState { - case model.PinStateTypeNone: - c.setAndHandleState(SmePinStateCheckOk) - case model.PinStateTypeRequired: - c.endHandshakeWithError(errors.New("Got pin state: required (unsupported)")) - case model.PinStateTypeOptional: - c.endHandshakeWithError(errors.New("Got pin state: optional (unsupported)")) - case model.PinStateTypePinOk: - c.endHandshakeWithError(errors.New("Got pin state: ok (unsupported)")) - default: - c.endHandshakeWithError(errors.New("Got invalid pin state")) - } -} diff --git a/ship/hs_pin_test.go b/ship/hs_pin_test.go deleted file mode 100644 index 5098e6b8..00000000 --- a/ship/hs_pin_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package ship - -import ( - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestPinSuite(t *testing.T) { - suite.Run(t, new(PinSuite)) -} - -type PinSuite struct { - suite.Suite -} - -func (s *PinSuite) Test_Init() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmePinStateCheckInit, nil) - sut.handleState(false, nil) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmePinStateCheckListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *PinSuite) Test_CheckListen_None() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmePinStateCheckListen, nil) - - pinState := model.ConnectionPinState{ - ConnectionPinState: model.ConnectionPinStateType{ - PinState: model.PinStateTypeNone, - }, - } - msg, err := sut.shipMessage(model.MsgTypeControl, pinState) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeAccessMethodsRequest, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *PinSuite) Test_CheckListen_Required() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmePinStateCheckListen, nil) - - pinState := model.ConnectionPinState{ - ConnectionPinState: model.ConnectionPinStateType{ - PinState: model.PinStateTypeRequired, - }, - } - msg, err := sut.shipMessage(model.MsgTypeControl, pinState) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *PinSuite) Test_CheckListen_Optional() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmePinStateCheckListen, nil) - - pinState := model.ConnectionPinState{ - ConnectionPinState: model.ConnectionPinStateType{ - PinState: model.PinStateTypeOptional, - }, - } - msg, err := sut.shipMessage(model.MsgTypeControl, pinState) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *PinSuite) Test_CheckListen_Ok() { - sut, data := initTest(ShipRoleClient) - - sut.setState(SmePinStateCheckListen, nil) - - pinState := model.ConnectionPinState{ - ConnectionPinState: model.ConnectionPinStateType{ - PinState: model.PinStateTypePinOk, - }, - } - msg, err := sut.shipMessage(model.MsgTypeControl, pinState) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} diff --git a/ship/hs_prot.go b/ship/hs_prot.go deleted file mode 100644 index 0f5c6de9..00000000 --- a/ship/hs_prot.go +++ /dev/null @@ -1,175 +0,0 @@ -package ship - -import ( - "encoding/json" - "errors" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/ship/model" -) - -// Handshake Prot covers the states smeProt... - -func (c *ShipConnectionImpl) handshakeProtocol_Init() { - switch c.role { - case ShipRoleServer: - c.setState(SmeProtHStateServerInit, nil) - c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) - c.setState(SmeProtHStateServerListenProposal, nil) - case ShipRoleClient: - c.setState(SmeProtHStateClientInit, nil) - c.handshakeProtocol_smeProtHStateClientInit() - } -} - -// provide a ship.MessageProtocolHandshake struct -func (c *ShipConnectionImpl) protocolHandshake() model.MessageProtocolHandshake { - protocolHandshake := model.MessageProtocolHandshake{ - MessageProtocolHandshake: model.MessageProtocolHandshakeType{ - Version: model.Version{Major: 1, Minor: 0}, - Formats: model.MessageProtocolFormatsType{ - Format: []model.MessageProtocolFormatType{model.MessageProtocolFormatTypeUTF8}, - }, - }, - } - - return protocolHandshake -} - -func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateServerListenProposal(message []byte) { - _, data := c.parseMessage(message, true) - - messageProtocolHandshake := model.MessageProtocolHandshake{} - if err := json.Unmarshal([]byte(data), &messageProtocolHandshake); err != nil { - c.endHandshakeWithError(err) - return - } - - if messageProtocolHandshake.MessageProtocolHandshake.HandshakeType != model.ProtocolHandshakeTypeTypeAnnounceMax { - c.endHandshakeWithError(errors.New("Invalid protocol handshake request")) - return - } - - c.stopHandshakeTimer() - - protocolHandshake := c.protocolHandshake() - protocolHandshake.MessageProtocolHandshake.HandshakeType = model.ProtocolHandshakeTypeTypeSelect - - if err := c.sendShipModel(model.MsgTypeControl, protocolHandshake); err != nil { - c.endHandshakeWithError(err) - } - - c.setHandshakeTimer(timeoutTimerTypeWaitForReady, cmiTimeout) - - c.setState(SmeProtHStateServerListenConfirm, nil) -} - -func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateServerListenConfirm(message []byte) { - _, data := c.parseMessage(message, true) - - var messageProtocolHandshake model.MessageProtocolHandshake - if err := json.Unmarshal([]byte(data), &messageProtocolHandshake); err != nil { - logging.Log().Debug(err) - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeUnexpectedMessage) - return - } - - if messageProtocolHandshake.MessageProtocolHandshake.HandshakeType != model.ProtocolHandshakeTypeTypeSelect { - logging.Log().Debug("invalid protocol handshake response") - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) - return - } - - c.stopHandshakeTimer() - - c.setAndHandleState(SmeProtHStateServerOk) -} - -func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateClientInit() { - c.setState(SmeProtHStateClientInit, nil) - - protocolHandshake := c.protocolHandshake() - protocolHandshake.MessageProtocolHandshake.HandshakeType = model.ProtocolHandshakeTypeTypeAnnounceMax - - if err := c.sendShipModel(model.MsgTypeControl, protocolHandshake); err != nil { - c.endHandshakeWithError(err) - return - } - - c.setState(SmeProtHStateClientListenChoice, nil) -} - -func (c *ShipConnectionImpl) handshakeProtocol_smeProtHStateClientListenChoice(message []byte) { - _, data := c.parseMessage(message, true) - - messageProtocolHandshake := model.MessageProtocolHandshake{} - if err := json.Unmarshal([]byte(data), &messageProtocolHandshake); err != nil { - logging.Log().Debug(err) - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeUnexpectedMessage) - return - } - - msgHandshake := messageProtocolHandshake.MessageProtocolHandshake - - abort := false - if msgHandshake.HandshakeType != model.ProtocolHandshakeTypeTypeSelect { - logging.Log().Debug("invalid protocol handshake response") - abort = true - } - - if msgHandshake.Version.Major != 1 { - logging.Log().Debug("unsupported protocol major version") - abort = true - } - - if msgHandshake.Version.Minor != 0 { - logging.Log().Debug("unsupported protocol minor version") - abort = true - } - - if msgHandshake.Formats.Format == nil || len(msgHandshake.Formats.Format) == 0 { - logging.Log().Debug("format is missing") - abort = true - } - - if len(msgHandshake.Formats.Format) != 1 { - logging.Log().Debug("unsupported format response") - abort = true - } - - if msgHandshake.Formats.Format != nil && msgHandshake.Formats.Format[0] != model.MessageProtocolFormatTypeUTF8 { - logging.Log().Debug("unsupported format") - abort = true - } - - if abort { - c.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeSelectionMismatch) - return - } - - c.stopHandshakeTimer() - - protocolHandshake := c.protocolHandshake() - protocolHandshake.MessageProtocolHandshake.HandshakeType = model.ProtocolHandshakeTypeTypeSelect - - if err := c.sendShipModel(model.MsgTypeControl, protocolHandshake); err != nil { - c.endHandshakeWithError(err) - return - } - - c.setAndHandleState(SmeProtHStateClientOk) -} - -func (c *ShipConnectionImpl) abortProtocolHandshake(err model.MessageProtocolHandshakeErrorErrorType) { - c.stopHandshakeTimer() - - msg := model.MessageProtocolHandshakeError{ - Error: err, - } - - _ = c.sendShipModel(model.MsgTypeControl, msg) - - c.setState(SmeStateError, errors.New("handshake error")) - - c.CloseConnection(false, 0, "") -} diff --git a/ship/hs_prot_client_test.go b/ship/hs_prot_client_test.go deleted file mode 100644 index dd8d074c..00000000 --- a/ship/hs_prot_client_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package ship - -import ( - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestProClientSuite(t *testing.T) { - suite.Run(t, new(ProClientSuite)) -} - -type ProClientSuite struct { - suite.Suite - - role shipRole -} - -func (s *ProClientSuite) BeforeTest(suiteName, testName string) { - s.role = ShipRoleClient -} - -func (s *ProClientSuite) Test_Init() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStateOk, nil) - - sut.handleState(false, nil) - - // the state goes from smeHelloStateOk to smeProtHStateClientInit to smeProtHStateClientListenChoice - assert.Equal(s.T(), SmeProtHStateClientListenChoice, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *ProClientSuite) Test_ListenChoice() { - sut, data := initTest(s.role) - - sut.setState(SmeProtHStateClientListenChoice, nil) - - protMsg := model.MessageProtocolHandshake{ - MessageProtocolHandshake: model.MessageProtocolHandshakeType{ - HandshakeType: model.ProtocolHandshakeTypeTypeSelect, - Version: model.Version{Major: 1, Minor: 0}, - Formats: model.MessageProtocolFormatsType{ - Format: []model.MessageProtocolFormatType{model.MessageProtocolFormatTypeUTF8}, - }, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - - // state goes directly from smeProtHStateClientOk to smePinStateCheckInit to smePinStateCheckListen - assert.Equal(s.T(), SmePinStateCheckListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *ProClientSuite) Test_ListenChoice_Failures() { - sut, data := initTest(s.role) - - sut.setState(SmeProtHStateClientListenChoice, nil) - - protMsg := model.MessageProtocolHandshake{ - MessageProtocolHandshake: model.MessageProtocolHandshakeType{ - HandshakeType: model.ProtocolHandshakeTypeTypeAnnounceMax, - Version: model.Version{Major: 0, Minor: 1}, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - sut.setState(SmeProtHStateClientListenChoice, nil) - - protMsg = model.MessageProtocolHandshake{ - MessageProtocolHandshake: model.MessageProtocolHandshakeType{ - HandshakeType: model.ProtocolHandshakeTypeTypeAnnounceMax, - Version: model.Version{Major: 0, Minor: 1}, - Formats: model.MessageProtocolFormatsType{ - Format: []model.MessageProtocolFormatType{model.MessageProtocolFormatTypeUTF16}, - }, - }, - } - - msg, err = sut.shipMessage(model.MsgTypeControl, protMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *ProClientSuite) Test_Abort() { - sut, data := initTest(s.role) - - sut.setState(SmeProtHStateClientListenChoice, nil) - - sut.abortProtocolHandshake(model.MessageProtocolHandshakeErrorErrorTypeTimeout) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - timer := sut.getHandshakeTimerRunnging() - assert.Equal(s.T(), false, timer) - - shutdownTest(sut) -} diff --git a/ship/hs_prot_server_test.go b/ship/hs_prot_server_test.go deleted file mode 100644 index a42fdb0a..00000000 --- a/ship/hs_prot_server_test.go +++ /dev/null @@ -1,146 +0,0 @@ -package ship - -import ( - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestProServerSuite(t *testing.T) { - suite.Run(t, new(ProServerSuite)) -} - -type ProServerSuite struct { - suite.Suite - role shipRole -} - -func (s *ProServerSuite) BeforeTest(suiteName, testName string) { - s.role = ShipRoleServer -} - -func (s *ProServerSuite) Test_Init() { - sut, data := initTest(s.role) - - sut.setState(SmeHelloStateOk, nil) - - sut.handleState(false, nil) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - - // the state goes from smeHelloStateOk to smeProtHStateServerInit to smeProtHStateServerListenProposal - assert.Equal(s.T(), SmeProtHStateServerListenProposal, sut.getState()) - assert.Nil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *ProServerSuite) Test_ListenProposal() { - sut, data := initTest(s.role) - - sut.setState(SmeProtHStateServerListenProposal, nil) - - protMsg := model.MessageProtocolHandshake{ - MessageProtocolHandshake: model.MessageProtocolHandshakeType{ - HandshakeType: model.ProtocolHandshakeTypeTypeAnnounceMax, - Version: model.Version{Major: 1, Minor: 0}, - Formats: model.MessageProtocolFormatsType{ - Format: []model.MessageProtocolFormatType{model.MessageProtocolFormatTypeUTF8}, - }, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), true, sut.handshakeTimerRunning) - - assert.Equal(s.T(), SmeProtHStateServerListenConfirm, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *ProServerSuite) Test_ListenProposal_Failure() { - sut, _ := initTest(s.role) - - sut.setState(SmeProtHStateServerListenProposal, nil) - - protMsg := model.MessageProtocolHandshake{ - MessageProtocolHandshake: model.MessageProtocolHandshakeType{ - HandshakeType: model.ProtocolHandshakeTypeTypeSelect, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - - shutdownTest(sut) -} - -func (s *ProServerSuite) Test_ListenConfirm() { - sut, data := initTest(s.role) - - sut.setState(SmeProtHStateServerListenConfirm, nil) - - protMsg := model.MessageProtocolHandshake{ - MessageProtocolHandshake: model.MessageProtocolHandshakeType{ - HandshakeType: model.ProtocolHandshakeTypeTypeSelect, - Version: model.Version{Major: 1, Minor: 0}, - Formats: model.MessageProtocolFormatsType{ - Format: []model.MessageProtocolFormatType{model.MessageProtocolFormatTypeUTF8}, - }, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - - // state smeProtHStateServerOk directly goes to smePinStateCheckInit to smePinStateCheckListen - assert.Equal(s.T(), SmePinStateCheckListen, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} - -func (s *ProServerSuite) Test_ListenConfirm_Failures() { - sut, data := initTest(s.role) - - sut.setState(SmeProtHStateServerListenConfirm, nil) - - protMsg := model.MessageProtocolHandshake{ - MessageProtocolHandshake: model.MessageProtocolHandshakeType{ - HandshakeType: model.ProtocolHandshakeTypeTypeAnnounceMax, - }, - } - - msg, err := sut.shipMessage(model.MsgTypeControl, protMsg) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), msg) - - sut.handleState(false, msg) - - assert.Equal(s.T(), false, sut.handshakeTimerRunning) - - assert.Equal(s.T(), SmeStateError, sut.getState()) - assert.NotNil(s.T(), data.lastMessage()) - - shutdownTest(sut) -} diff --git a/ship/mock_types_test.go b/ship/mock_types_test.go deleted file mode 100644 index e1abe9d3..00000000 --- a/ship/mock_types_test.go +++ /dev/null @@ -1,310 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/enbility/eebus-go/ship (interfaces: WebsocketDataConnection,WebsocketDataProcessing,ShipServiceDataProvider,SpineDataProcessing,SpineDataConnection) -// -// Generated by this command: -// -// mockgen -destination=mock_types_test.go -package=ship github.com/enbility/eebus-go/ship WebsocketDataConnection,WebsocketDataProcessing,ShipServiceDataProvider,SpineDataProcessing,SpineDataConnection -// - -// Package ship is a generated GoMock package. -package ship - -import ( - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockWebsocketDataConnection is a mock of WebsocketDataConnection interface. -type MockWebsocketDataConnection struct { - ctrl *gomock.Controller - recorder *MockWebsocketDataConnectionMockRecorder -} - -// MockWebsocketDataConnectionMockRecorder is the mock recorder for MockWebsocketDataConnection. -type MockWebsocketDataConnectionMockRecorder struct { - mock *MockWebsocketDataConnection -} - -// NewMockWebsocketDataConnection creates a new mock instance. -func NewMockWebsocketDataConnection(ctrl *gomock.Controller) *MockWebsocketDataConnection { - mock := &MockWebsocketDataConnection{ctrl: ctrl} - mock.recorder = &MockWebsocketDataConnectionMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockWebsocketDataConnection) EXPECT() *MockWebsocketDataConnectionMockRecorder { - return m.recorder -} - -// CloseDataConnection mocks base method. -func (m *MockWebsocketDataConnection) CloseDataConnection(arg0 int, arg1 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "CloseDataConnection", arg0, arg1) -} - -// CloseDataConnection indicates an expected call of CloseDataConnection. -func (mr *MockWebsocketDataConnectionMockRecorder) CloseDataConnection(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseDataConnection", reflect.TypeOf((*MockWebsocketDataConnection)(nil).CloseDataConnection), arg0, arg1) -} - -// InitDataProcessing mocks base method. -func (m *MockWebsocketDataConnection) InitDataProcessing(arg0 WebsocketDataProcessing) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "InitDataProcessing", arg0) -} - -// InitDataProcessing indicates an expected call of InitDataProcessing. -func (mr *MockWebsocketDataConnectionMockRecorder) InitDataProcessing(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitDataProcessing", reflect.TypeOf((*MockWebsocketDataConnection)(nil).InitDataProcessing), arg0) -} - -// IsDataConnectionClosed mocks base method. -func (m *MockWebsocketDataConnection) IsDataConnectionClosed() (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsDataConnectionClosed") - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsDataConnectionClosed indicates an expected call of IsDataConnectionClosed. -func (mr *MockWebsocketDataConnectionMockRecorder) IsDataConnectionClosed() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDataConnectionClosed", reflect.TypeOf((*MockWebsocketDataConnection)(nil).IsDataConnectionClosed)) -} - -// WriteMessageToDataConnection mocks base method. -func (m *MockWebsocketDataConnection) WriteMessageToDataConnection(arg0 []byte) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WriteMessageToDataConnection", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// WriteMessageToDataConnection indicates an expected call of WriteMessageToDataConnection. -func (mr *MockWebsocketDataConnectionMockRecorder) WriteMessageToDataConnection(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteMessageToDataConnection", reflect.TypeOf((*MockWebsocketDataConnection)(nil).WriteMessageToDataConnection), arg0) -} - -// MockWebsocketDataProcessing is a mock of WebsocketDataProcessing interface. -type MockWebsocketDataProcessing struct { - ctrl *gomock.Controller - recorder *MockWebsocketDataProcessingMockRecorder -} - -// MockWebsocketDataProcessingMockRecorder is the mock recorder for MockWebsocketDataProcessing. -type MockWebsocketDataProcessingMockRecorder struct { - mock *MockWebsocketDataProcessing -} - -// NewMockWebsocketDataProcessing creates a new mock instance. -func NewMockWebsocketDataProcessing(ctrl *gomock.Controller) *MockWebsocketDataProcessing { - mock := &MockWebsocketDataProcessing{ctrl: ctrl} - mock.recorder = &MockWebsocketDataProcessingMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockWebsocketDataProcessing) EXPECT() *MockWebsocketDataProcessingMockRecorder { - return m.recorder -} - -// HandleIncomingShipMessage mocks base method. -func (m *MockWebsocketDataProcessing) HandleIncomingShipMessage(arg0 []byte) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "HandleIncomingShipMessage", arg0) -} - -// HandleIncomingShipMessage indicates an expected call of HandleIncomingShipMessage. -func (mr *MockWebsocketDataProcessingMockRecorder) HandleIncomingShipMessage(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleIncomingShipMessage", reflect.TypeOf((*MockWebsocketDataProcessing)(nil).HandleIncomingShipMessage), arg0) -} - -// ReportConnectionError mocks base method. -func (m *MockWebsocketDataProcessing) ReportConnectionError(arg0 error) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ReportConnectionError", arg0) -} - -// ReportConnectionError indicates an expected call of ReportConnectionError. -func (mr *MockWebsocketDataProcessingMockRecorder) ReportConnectionError(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportConnectionError", reflect.TypeOf((*MockWebsocketDataProcessing)(nil).ReportConnectionError), arg0) -} - -// MockShipServiceDataProvider is a mock of ShipServiceDataProvider interface. -type MockShipServiceDataProvider struct { - ctrl *gomock.Controller - recorder *MockShipServiceDataProviderMockRecorder -} - -// MockShipServiceDataProviderMockRecorder is the mock recorder for MockShipServiceDataProvider. -type MockShipServiceDataProviderMockRecorder struct { - mock *MockShipServiceDataProvider -} - -// NewMockShipServiceDataProvider creates a new mock instance. -func NewMockShipServiceDataProvider(ctrl *gomock.Controller) *MockShipServiceDataProvider { - mock := &MockShipServiceDataProvider{ctrl: ctrl} - mock.recorder = &MockShipServiceDataProviderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockShipServiceDataProvider) EXPECT() *MockShipServiceDataProviderMockRecorder { - return m.recorder -} - -// AllowWaitingForTrust mocks base method. -func (m *MockShipServiceDataProvider) AllowWaitingForTrust(arg0 string) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AllowWaitingForTrust", arg0) - ret0, _ := ret[0].(bool) - return ret0 -} - -// AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. -func (mr *MockShipServiceDataProviderMockRecorder) AllowWaitingForTrust(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockShipServiceDataProvider)(nil).AllowWaitingForTrust), arg0) -} - -// HandleConnectionClosed mocks base method. -func (m *MockShipServiceDataProvider) HandleConnectionClosed(arg0 ShipConnection, arg1 bool) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "HandleConnectionClosed", arg0, arg1) -} - -// HandleConnectionClosed indicates an expected call of HandleConnectionClosed. -func (mr *MockShipServiceDataProviderMockRecorder) HandleConnectionClosed(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleConnectionClosed", reflect.TypeOf((*MockShipServiceDataProvider)(nil).HandleConnectionClosed), arg0, arg1) -} - -// HandleShipHandshakeStateUpdate mocks base method. -func (m *MockShipServiceDataProvider) HandleShipHandshakeStateUpdate(arg0 string, arg1 ShipState) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "HandleShipHandshakeStateUpdate", arg0, arg1) -} - -// HandleShipHandshakeStateUpdate indicates an expected call of HandleShipHandshakeStateUpdate. -func (mr *MockShipServiceDataProviderMockRecorder) HandleShipHandshakeStateUpdate(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleShipHandshakeStateUpdate", reflect.TypeOf((*MockShipServiceDataProvider)(nil).HandleShipHandshakeStateUpdate), arg0, arg1) -} - -// IsRemoteServiceForSKIPaired mocks base method. -func (m *MockShipServiceDataProvider) IsRemoteServiceForSKIPaired(arg0 string) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsRemoteServiceForSKIPaired", arg0) - ret0, _ := ret[0].(bool) - return ret0 -} - -// IsRemoteServiceForSKIPaired indicates an expected call of IsRemoteServiceForSKIPaired. -func (mr *MockShipServiceDataProviderMockRecorder) IsRemoteServiceForSKIPaired(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsRemoteServiceForSKIPaired", reflect.TypeOf((*MockShipServiceDataProvider)(nil).IsRemoteServiceForSKIPaired), arg0) -} - -// ReportServiceShipID mocks base method. -func (m *MockShipServiceDataProvider) ReportServiceShipID(arg0, arg1 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ReportServiceShipID", arg0, arg1) -} - -// ReportServiceShipID indicates an expected call of ReportServiceShipID. -func (mr *MockShipServiceDataProviderMockRecorder) ReportServiceShipID(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportServiceShipID", reflect.TypeOf((*MockShipServiceDataProvider)(nil).ReportServiceShipID), arg0, arg1) -} - -// SetupRemoteDevice mocks base method. -func (m *MockShipServiceDataProvider) SetupRemoteDevice(arg0 string, arg1 SpineDataConnection) SpineDataProcessing { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetupRemoteDevice", arg0, arg1) - ret0, _ := ret[0].(SpineDataProcessing) - return ret0 -} - -// SetupRemoteDevice indicates an expected call of SetupRemoteDevice. -func (mr *MockShipServiceDataProviderMockRecorder) SetupRemoteDevice(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupRemoteDevice", reflect.TypeOf((*MockShipServiceDataProvider)(nil).SetupRemoteDevice), arg0, arg1) -} - -// MockSpineDataProcessing is a mock of SpineDataProcessing interface. -type MockSpineDataProcessing struct { - ctrl *gomock.Controller - recorder *MockSpineDataProcessingMockRecorder -} - -// MockSpineDataProcessingMockRecorder is the mock recorder for MockSpineDataProcessing. -type MockSpineDataProcessingMockRecorder struct { - mock *MockSpineDataProcessing -} - -// NewMockSpineDataProcessing creates a new mock instance. -func NewMockSpineDataProcessing(ctrl *gomock.Controller) *MockSpineDataProcessing { - mock := &MockSpineDataProcessing{ctrl: ctrl} - mock.recorder = &MockSpineDataProcessingMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSpineDataProcessing) EXPECT() *MockSpineDataProcessingMockRecorder { - return m.recorder -} - -// HandleIncomingSpineMesssage mocks base method. -func (m *MockSpineDataProcessing) HandleIncomingSpineMesssage(arg0 []byte) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "HandleIncomingSpineMesssage", arg0) -} - -// HandleIncomingSpineMesssage indicates an expected call of HandleIncomingSpineMesssage. -func (mr *MockSpineDataProcessingMockRecorder) HandleIncomingSpineMesssage(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleIncomingSpineMesssage", reflect.TypeOf((*MockSpineDataProcessing)(nil).HandleIncomingSpineMesssage), arg0) -} - -// MockSpineDataConnection is a mock of SpineDataConnection interface. -type MockSpineDataConnection struct { - ctrl *gomock.Controller - recorder *MockSpineDataConnectionMockRecorder -} - -// MockSpineDataConnectionMockRecorder is the mock recorder for MockSpineDataConnection. -type MockSpineDataConnectionMockRecorder struct { - mock *MockSpineDataConnection -} - -// NewMockSpineDataConnection creates a new mock instance. -func NewMockSpineDataConnection(ctrl *gomock.Controller) *MockSpineDataConnection { - mock := &MockSpineDataConnection{ctrl: ctrl} - mock.recorder = &MockSpineDataConnectionMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSpineDataConnection) EXPECT() *MockSpineDataConnectionMockRecorder { - return m.recorder -} - -// WriteSpineMessage mocks base method. -func (m *MockSpineDataConnection) WriteSpineMessage(arg0 []byte) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "WriteSpineMessage", arg0) -} - -// WriteSpineMessage indicates an expected call of WriteSpineMessage. -func (mr *MockSpineDataConnectionMockRecorder) WriteSpineMessage(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteSpineMessage", reflect.TypeOf((*MockSpineDataConnection)(nil).WriteSpineMessage), arg0) -} diff --git a/ship/mocks/ShipConnection.go b/ship/mocks/ShipConnection.go deleted file mode 100644 index afd5fc3a..00000000 --- a/ship/mocks/ShipConnection.go +++ /dev/null @@ -1,108 +0,0 @@ -// Code generated by mockery v2.39.1. DO NOT EDIT. - -package mocks - -import ( - ship "github.com/enbility/eebus-go/ship" - mock "github.com/stretchr/testify/mock" -) - -// ShipConnection is an autogenerated mock type for the ShipConnection type -type ShipConnection struct { - mock.Mock -} - -// AbortPendingHandshake provides a mock function with given fields: -func (_m *ShipConnection) AbortPendingHandshake() { - _m.Called() -} - -// ApprovePendingHandshake provides a mock function with given fields: -func (_m *ShipConnection) ApprovePendingHandshake() { - _m.Called() -} - -// CloseConnection provides a mock function with given fields: safe, code, reason -func (_m *ShipConnection) CloseConnection(safe bool, code int, reason string) { - _m.Called(safe, code, reason) -} - -// DataHandler provides a mock function with given fields: -func (_m *ShipConnection) DataHandler() ship.WebsocketDataConnection { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DataHandler") - } - - var r0 ship.WebsocketDataConnection - if rf, ok := ret.Get(0).(func() ship.WebsocketDataConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(ship.WebsocketDataConnection) - } - } - - return r0 -} - -// RemoteSKI provides a mock function with given fields: -func (_m *ShipConnection) RemoteSKI() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for RemoteSKI") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// ShipHandshakeState provides a mock function with given fields: -func (_m *ShipConnection) ShipHandshakeState() (ship.ShipMessageExchangeState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ShipHandshakeState") - } - - var r0 ship.ShipMessageExchangeState - var r1 error - if rf, ok := ret.Get(0).(func() (ship.ShipMessageExchangeState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() ship.ShipMessageExchangeState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ship.ShipMessageExchangeState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewShipConnection creates a new instance of ShipConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewShipConnection(t interface { - mock.TestingT - Cleanup(func()) -}) *ShipConnection { - mock := &ShipConnection{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/ship/mocks/WebsocketDataConnection.go b/ship/mocks/WebsocketDataConnection.go deleted file mode 100644 index f796e13b..00000000 --- a/ship/mocks/WebsocketDataConnection.go +++ /dev/null @@ -1,83 +0,0 @@ -// Code generated by mockery v2.39.1. DO NOT EDIT. - -package mocks - -import ( - ship "github.com/enbility/eebus-go/ship" - mock "github.com/stretchr/testify/mock" -) - -// WebsocketDataConnection is an autogenerated mock type for the WebsocketDataConnection type -type WebsocketDataConnection struct { - mock.Mock -} - -// CloseDataConnection provides a mock function with given fields: closeCode, reason -func (_m *WebsocketDataConnection) CloseDataConnection(closeCode int, reason string) { - _m.Called(closeCode, reason) -} - -// InitDataProcessing provides a mock function with given fields: _a0 -func (_m *WebsocketDataConnection) InitDataProcessing(_a0 ship.WebsocketDataProcessing) { - _m.Called(_a0) -} - -// IsDataConnectionClosed provides a mock function with given fields: -func (_m *WebsocketDataConnection) IsDataConnectionClosed() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for IsDataConnectionClosed") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// WriteMessageToDataConnection provides a mock function with given fields: _a0 -func (_m *WebsocketDataConnection) WriteMessageToDataConnection(_a0 []byte) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for WriteMessageToDataConnection") - } - - var r0 error - if rf, ok := ret.Get(0).(func([]byte) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// NewWebsocketDataConnection creates a new instance of WebsocketDataConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewWebsocketDataConnection(t interface { - mock.TestingT - Cleanup(func()) -}) *WebsocketDataConnection { - mock := &WebsocketDataConnection{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/ship/model/model.go b/ship/model/model.go deleted file mode 100644 index 2eb8710e..00000000 --- a/ship/model/model.go +++ /dev/null @@ -1,189 +0,0 @@ -package model - -import "encoding/json" - -const ( - MsgTypeInit byte = 0 - MsgTypeControl byte = 1 - MsgTypeData byte = 2 - MsgTypeEnd byte = 3 -) - -const ( - ShipProtocolId = "ee1.0" -) - -type ConnectionHelloPhaseType string - -const ( - ConnectionHelloPhaseTypePending ConnectionHelloPhaseType = "pending" - ConnectionHelloPhaseTypeReady ConnectionHelloPhaseType = "ready" - ConnectionHelloPhaseTypeAborted ConnectionHelloPhaseType = "aborted" -) - -type ConnectionHello struct { - ConnectionHello ConnectionHelloType `json:"connectionHello"` -} - -type ConnectionHelloType struct { - Phase ConnectionHelloPhaseType `json:"phase"` - Waiting *uint `json:"waiting,omitempty"` - ProlongationRequest *bool `json:"prolongationRequest,omitempty"` -} - -type MessageProtocolFormatType string - -const ( - MessageProtocolFormatTypeUTF8 MessageProtocolFormatType = "JSON-UTF8" - MessageProtocolFormatTypeUTF16 MessageProtocolFormatType = "JSON-UTF16" -) - -type MessageProtocolFormatsType struct { - Format []MessageProtocolFormatType `json:"format"` -} - -type ProtocolHandshakeTypeType string - -const ( - ProtocolHandshakeTypeTypeAnnounceMax ProtocolHandshakeTypeType = "announceMax" - ProtocolHandshakeTypeTypeSelect ProtocolHandshakeTypeType = "select" -) - -type Version struct { - Major uint8 `json:"major"` - Minor uint8 `json:"minor"` -} - -type MessageProtocolHandshakeType struct { - HandshakeType ProtocolHandshakeTypeType `json:"handshakeType"` - Version Version `json:"version"` - Formats MessageProtocolFormatsType `json:"formats"` -} - -type MessageProtocolHandshake struct { - MessageProtocolHandshake MessageProtocolHandshakeType `json:"messageProtocolHandshake"` -} - -type MessageProtocolHandshakeErrorErrorType uint8 - -const ( - MessageProtocolHandshakeErrorErrorTypeRFU MessageProtocolHandshakeErrorErrorType = 0 - MessageProtocolHandshakeErrorErrorTypeTimeout MessageProtocolHandshakeErrorErrorType = 1 - MessageProtocolHandshakeErrorErrorTypeUnexpectedMessage MessageProtocolHandshakeErrorErrorType = 2 - MessageProtocolHandshakeErrorErrorTypeSelectionMismatch MessageProtocolHandshakeErrorErrorType = 3 -) - -type MessageProtocolHandshakeErrorType struct { - Error MessageProtocolHandshakeErrorErrorType `json:"error"` -} - -type PinStateType string - -const ( - PinStateTypeRequired PinStateType = "required" - PinStateTypeOptional PinStateType = "optional" - PinStateTypePinOk PinStateType = "pinOk" - PinStateTypeNone PinStateType = "none" -) - -type PinInputPermissionType string - -const ( - PinInputPermissionTypeBusy PinInputPermissionType = "busy" - PinInputPermissionTypeOk PinInputPermissionType = "ok" -) - -type MessageProtocolHandshakeError struct { - Error MessageProtocolHandshakeErrorErrorType `json:"error"` -} - -type ConnectionPinStateType struct { - PinState PinStateType `json:"pinState"` - InputPermission *PinInputPermissionType `json:"inputPermission,omitempty"` -} - -type ConnectionPinState struct { - ConnectionPinState ConnectionPinStateType `json:"connectionPinState"` -} - -type PinValueType string - -type ConnectionPinInputType struct { - Pin PinValueType `json:"pin"` -} - -type ConnectionPinErrorErrorType uint8 - -type ConnectionPinErrorType struct { - Error ConnectionPinErrorErrorType `json:"error"` -} - -type ProtocolIdType string - -type HeaderType struct { - ProtocolId ProtocolIdType `json:"protocolId"` -} - -type ExtensionType struct { - ExtensionId *string `json:"extensionId,omitempty"` - Binary *byte `json:"binary,omitempty"` // HexBinary - String *string `json:"string,omitempty"` -} - -type ShipData struct { - Data DataType `json:"data"` -} - -type DataType struct { - Header HeaderType `json:"header"` - Payload json.RawMessage `json:"payload"` - Extension *ExtensionType `json:"extension,omitempty"` -} - -type ConnectionClosePhaseType string - -const ( - ConnectionClosePhaseTypeAnnounce ConnectionClosePhaseType = "announce" - ConnectionClosePhaseTypeConfirm ConnectionClosePhaseType = "confirm" -) - -type ConnectionCloseReasonType string - -const ( - ConnectionCloseReasonTypeUnspecific ConnectionCloseReasonType = "unspecific" - ConnectionCloseReasonTypeRemovedconnection ConnectionCloseReasonType = "removedConnection" -) - -type ConnectionClose struct { - ConnectionClose ConnectionCloseType `json:"connectionClose"` -} - -type ConnectionCloseType struct { - Phase ConnectionClosePhaseType `json:"phase"` - MaxTime *uint `json:"maxTime,omitempty"` - Reason *ConnectionCloseReasonType `json:"reason,omitempty"` -} - -type AccessMethodsRequest struct { - AccessMethodsRequest AccessMethodsRequestType `json:"accessMethodsRequest"` -} - -type AccessMethodsRequestType struct{} - -type Dns struct { - Uri string `json:"uri"` -} - -type DnsSdMDns struct { -} - -type AccessMethods struct { - AccessMethods AccessMethodsType `json:"accessMethods"` -} - -type AccessMethodsType struct { - Id *string `json:"id"` - DnsSdMDns *DnsSdMDns `json:"dnsSd_mDns,omitempty"` - // According to the Spec Dns should be of type *Dns, but the SHM 2.0 only uses a string and would cause a crash - Dns *string `json:"dns,omitempty"` -} diff --git a/ship/types.go b/ship/types.go deleted file mode 100644 index a6ca4d34..00000000 --- a/ship/types.go +++ /dev/null @@ -1,111 +0,0 @@ -package ship - -import ( - "time" - - "github.com/enbility/eebus-go/ship/model" -) - -type shipRole string - -const ( - ShipRoleServer shipRole = "server" - ShipRoleClient shipRole = "client" -) - -const ( - writeWait = 10 * time.Second - - // Time allowed to read the next pong message from the peer. - pongWait = 60 * time.Second // SHIP 4.2: ping interval + pong timeout - // Send pings to peer with this period. Must be less than pongWait. - pingPeriod = 50 * time.Second // SHIP 4.2: ping interval - - // SHIP 9.2: Set maximum fragment length to 1024 bytes - MaxMessageSize = 1024 -) - -const ( - cmiTimeout = 10 * time.Second // SHIP 4.2 - cmiCloseTimeout = 100 * time.Millisecond - tHelloInit = 60 * time.Second // SHIP 13.4.4.1.3 - tHelloInc = 60 * time.Second - tHelloProlongThrInc = 30 * time.Second - tHelloProlongWaitingGap = 15 * time.Second - tHelloProlongMin = 1 * time.Second -) - -type timeoutTimerType uint - -const ( - // SHIP 13.4.4.1.3: The communication partner must send its "READY" state (or request for prolongation") before the timer expires. - timeoutTimerTypeWaitForReady timeoutTimerType = iota - // SHIP 13.4.4.1.3: Local timer to request for prolongation at the communication partner in time (i.e. before the communication partner's Wait-For-Ready-Timer expires). - timeoutTimerTypeSendProlongationRequest - // SHIP 13.4.4.1.3: Detection of response timeout on prolongation request. - timeoutTimerTypeProlongRequestReply -) - -type ShipState struct { - State ShipMessageExchangeState - Error error -} - -type ShipMessageExchangeState uint - -const ( - // Connection Mode Initialisation (CMI) SHIP 13.4.3 - CmiStateInitStart ShipMessageExchangeState = iota - CmiStateClientSend - CmiStateClientWait - CmiStateClientEvaluate - CmiStateServerWait - CmiStateServerEvaluate - // Connection Data Preparation SHIP 13.4.4 - SmeHelloState - SmeHelloStateReadyInit - SmeHelloStateReadyListen - SmeHelloStateReadyTimeout - SmeHelloStatePendingInit - SmeHelloStatePendingListen - SmeHelloStatePendingTimeout - SmeHelloStateOk - SmeHelloStateAbort // Sent abort to remote - SmeHelloStateAbortDone // Sending abort to remote is done - SmeHelloStateRemoteAbortDone // Received abort from remote - SmeHelloStateRejected // Connection closed after remote pending: "4452: Node rejected by application" - - // Connection State Protocol Handhsake SHIP 13.4.4.2 - SmeProtHStateServerInit - SmeProtHStateClientInit - SmeProtHStateServerListenProposal - SmeProtHStateServerListenConfirm - SmeProtHStateClientListenChoice - SmeProtHStateTimeout - SmeProtHStateClientOk - SmeProtHStateServerOk - // Connection PIN State 13.4.5 - SmePinStateCheckInit - SmePinStateCheckListen - SmePinStateCheckError - SmePinStateCheckBusyInit - SmePinStateCheckBusyWait - SmePinStateCheckOk - SmePinStateAskInit - SmePinStateAskProcess - SmePinStateAskRestricted - SmePinStateAskOk - // ConnectionAccess Methods Identification 13.4.6 - SmeAccessMethodsRequest - - // Handshake approved on both ends - SmeStateApproved - - // Handshake process is successfully completed - SmeStateComplete - - // Handshake ended with an error - SmeStateError -) - -var shipInit []byte = []byte{model.MsgTypeInit, 0x00} diff --git a/ship/util/helper.go b/ship/util/helper.go deleted file mode 100644 index a33458b1..00000000 --- a/ship/util/helper.go +++ /dev/null @@ -1,78 +0,0 @@ -package util - -import ( - "bytes" - "encoding/json" - "strings" - - "gitlab.com/c0b/go-ordered-json" -) - -// convert incoming EEBUS json format into standard json format -func JsonFromEEBUSJson(json []byte) []byte { - var result = bytes.ReplaceAll(json, []byte("[{"), []byte("{")) - result = bytes.ReplaceAll(result, []byte("},{"), []byte(",")) - result = bytes.ReplaceAll(result, []byte("}]"), []byte("}")) - result = bytes.ReplaceAll(result, []byte("[]"), []byte("{}")) - - return result -} - -// convert objects in json to be arrays with each field being an array alement as eebus expects it -func process_eebus_json_hierarchie_level(data interface{}) interface{} { - temp := data - switch temp.(type) { - case *ordered.OrderedMap: - var new_array []interface{} = make([]interface{}, 0) - - orderedData := data.(*ordered.OrderedMap) - iter := orderedData.EntriesIter() - for { - pair, ok := iter() - if !ok { - break - } - var new_value = process_eebus_json_hierarchie_level(pair.Value) - var new_object = map[string]interface{}{pair.Key: new_value} - new_array = append(new_array, new_object) - } - return new_array - - case []interface{}: - var new_array []interface{} = make([]interface{}, 0) - for _, value := range data.([]interface{}) { - var new_value = process_eebus_json_hierarchie_level(value) - new_array = append(new_array, new_value) - } - return new_array - default: - return data - } -} - -// convert json into the EEBUS json format -func JsonIntoEEBUSJson(data []byte) (string, error) { - // EEBUS defines the items to be ordered in the array, - // so we can't use map[string]interface{} as that would - // cause a random order when Unmarshalling - var temp *ordered.OrderedMap = ordered.NewOrderedMap() - - if err := json.Unmarshal(data, &temp); err != nil { - return "", err - } - - var result = process_eebus_json_hierarchie_level(temp) - - var b, err = json.Marshal(result) - if err != nil { - return "", err - } - - var json = string(b) - - // we are lazy: fix the first item being put into an array - json = strings.TrimPrefix(json, "[") - json = strings.TrimSuffix(json, "]") - - return json, nil -} diff --git a/ship/util/helper_test.go b/ship/util/helper_test.go deleted file mode 100644 index ae45a5a9..00000000 --- a/ship/util/helper_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package util_test - -import ( - "encoding/json" - "strings" - "testing" - - "github.com/enbility/eebus-go/ship/model" - "github.com/enbility/eebus-go/ship/util" -) - -func TestJsonFromEEBUSJson(t *testing.T) { - jsonTest := `{"datagram":[{"header":[{"specificationVersion":"1.2.0"},{"addressSource":[{"device":"d:_i:3210_EVSE"},{"entity":[1,1]},{"feature":6}]},{"addressDestination":[{"device":"d:_i:3210_HEMS"},{"entity":[1]},{"feature":1}]},{"msgCounter":194},{"msgCounterReference":4890},{"cmdClassifier":"reply"}]},{"payload":[{"cmd":[[{"deviceClassificationManufacturerData":[{"deviceName":""},{"deviceCode":""},{"brandName":""},{"powerSource":"mains3Phase"}]}]]}]}]}` - jsonExpected := `{"datagram":{"header":{"specificationVersion":"1.2.0","addressSource":{"device":"d:_i:3210_EVSE","entity":[1,1],"feature":6},"addressDestination":{"device":"d:_i:3210_HEMS","entity":[1],"feature":1},"msgCounter":194,"msgCounterReference":4890,"cmdClassifier":"reply"},"payload":{"cmd":[{"deviceClassificationManufacturerData":{"deviceName":"","deviceCode":"","brandName":"","powerSource":"mains3Phase"}}]}}}` - - var json = util.JsonFromEEBUSJson([]byte(jsonTest)) - - if string(json) != jsonExpected { - t.Errorf("\nExpected:\n %s\ngot:\n %s", jsonExpected, json) - } -} - -func TestJsonIntoEEBUSJson(t *testing.T) { - jsonTest := `{"datagram":{"header":{"specificationVersion":"1.2.0","addressSource":{"device":"d:_i:3210_EVSE","entity":[1,1],"feature":6},"addressDestination":{"device":"d:_i:3210_HEMS","entity":[1],"feature":1},"msgCounter":194,"msgCounterReference":4890,"cmdClassifier":"reply"},"payload":{"cmd":[{"deviceClassificationManufacturerData":{"deviceName":"","deviceCode":"","brandName":"","powerSource":"mains3Phase"}}]}}}` - jsonExpected := `{"datagram":[{"header":[{"specificationVersion":"1.2.0"},{"addressSource":[{"device":"d:_i:3210_EVSE"},{"entity":[1,1]},{"feature":6}]},{"addressDestination":[{"device":"d:_i:3210_HEMS"},{"entity":[1]},{"feature":1}]},{"msgCounter":194},{"msgCounterReference":4890},{"cmdClassifier":"reply"}]},{"payload":[{"cmd":[[{"deviceClassificationManufacturerData":[{"deviceName":""},{"deviceCode":""},{"brandName":""},{"powerSource":"mains3Phase"}]}]]}]}]}` - - var json, err = util.JsonIntoEEBUSJson([]byte(jsonTest)) - if err != nil { - println(err.Error()) - t.Errorf("\nExpected:\n %s\ngot:\n %s", jsonExpected, json) - } - - if json != jsonExpected { - t.Errorf("\nExpected:\n %s\ngot:\n %s", jsonExpected, json) - } -} - -const payloadPlaceholder = `{"place":"holder"}` - -func TestShipJsonIntoEEBUSJson(t *testing.T) { - spineTest := `{"datagram":{"header":{"specificationVersion":"1.2.0","addressSource":{"device":"Demo-EVSE-234567890","entity":[0],"feature":0},"addressDestination":{"device":"Demo-HEMS-123456789","entity":[0],"feature":0},"msgCounter":1,"cmdClassifier":"read"},"payload":{"cmd":[{"nodeManagementDetailedDiscoveryData":{}}]}}}` - jsonExpected := `{"data":[{"header":[{"protocolId":"ee1.0"}]},{"payload":{"datagram":[{"header":[{"specificationVersion":"1.2.0"},{"addressSource":[{"device":"Demo-EVSE-234567890"},{"entity":[0]},{"feature":0}]},{"addressDestination":[{"device":"Demo-HEMS-123456789"},{"entity":[0]},{"feature":0}]},{"msgCounter":1},{"cmdClassifier":"read"}]},{"payload":[{"cmd":[[{"nodeManagementDetailedDiscoveryData":[]}]]}]}]}}]}` - - // TODO: move this test into connection_test using "transformSpineDataIntoShipJson()" - spineMsg, err := util.JsonIntoEEBUSJson([]byte(spineTest)) - if err != nil { - t.Errorf(err.Error()) - } - payload := json.RawMessage([]byte(spineMsg)) - - shipMessage := model.ShipData{ - Data: model.DataType{ - Header: model.HeaderType{ - ProtocolId: model.ShipProtocolId, - }, - Payload: json.RawMessage([]byte(payloadPlaceholder)), - }, - } - - msg, err := json.Marshal(shipMessage) - if err != nil { - t.Errorf(err.Error()) - } - - json, err := util.JsonIntoEEBUSJson(msg) - if err != nil { - println(err.Error()) - t.Errorf("\nExpected:\n %s\ngot:\n %s", jsonExpected, json) - } - - json = strings.ReplaceAll(json, `[`+payloadPlaceholder+`]`, string(payload)) - - if json != jsonExpected { - t.Errorf("\nExpected:\n %s\ngot:\n %s", jsonExpected, json) - } -} diff --git a/ship/websocket.go b/ship/websocket.go deleted file mode 100644 index 61366559..00000000 --- a/ship/websocket.go +++ /dev/null @@ -1,303 +0,0 @@ -package ship - -import ( - "bytes" - "errors" - "fmt" - "sync" - "time" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/util" - "github.com/gorilla/websocket" -) - -// Handling of the actual websocket connection to a remote device -type websocketConnection struct { - // The actual websocket connection - conn *websocket.Conn - - // The implementation handling message processing - dataProcessing WebsocketDataProcessing - - // The connection was closed - closeChannel chan struct{} - - // The ship write channel for outgoing SHIP messages - shipWriteChannel chan []byte - - // internal handling of closed connections - connectionClosed bool - - // the error message received for the closed connection - connectionClosedError error - - remoteSki string - - muxConnClosed sync.Mutex - muxShipWrite sync.Mutex - muxConWrite sync.Mutex - shutdownOnce sync.Once -} - -// create a new websocket based shipDataProcessing implementation -func NewWebsocketConnection(conn *websocket.Conn, remoteSki string) *websocketConnection { - return &websocketConnection{ - conn: conn, - remoteSki: remoteSki, - connectionClosedError: nil, - } -} - -// sets the error message for the closed connection -func (w *websocketConnection) setConnClosedError(err error) { - w.muxConnClosed.Lock() - defer w.muxConnClosed.Unlock() - - w.connectionClosed = true - - if err != nil { - w.connectionClosedError = err - } -} - -func (w *websocketConnection) connClosedError() error { - w.muxConnClosed.Lock() - defer w.muxConnClosed.Unlock() - - return w.connectionClosedError -} - -// check if the websocket connection is closed -func (w *websocketConnection) isConnClosed() bool { - w.muxConnClosed.Lock() - defer w.muxConnClosed.Unlock() - - return w.connectionClosed -} - -func (w *websocketConnection) run() { - w.shipWriteChannel = make(chan []byte, 1) // Send outgoing ship messages - w.closeChannel = make(chan struct{}, 1) // Listen to close events - - go w.readShipPump() - go w.writeShipPump() -} - -// writePump pumps messages from the SPINE and SHIP writeChannels to the websocket connection -func (w *websocketConnection) writeShipPump() { - ticker := time.NewTicker(pingPeriod) - defer func() { - ticker.Stop() - }() - - for { - select { - case <-w.closeChannel: - return - - case message, ok := <-w.shipWriteChannel: - if w.isConnClosed() { - return - } - - w.muxConWrite.Lock() - _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) - w.muxConWrite.Unlock() - if !ok { - logging.Log().Debug(w.remoteSki, "ship write channel closed") - // The write channel has been closed - _ = w.writeMessage(websocket.CloseMessage, []byte{}) - return - } - - if err := w.writeMessage(websocket.BinaryMessage, message); err != nil { - // ignore write errors if the connection got closed - if w.isConnClosed() { - return - } - - w.closeWithError(err, "error writing to websocket: ") - return - } - - var text string - if len(message) > 2 { - text = string(message[1:]) - } else if bytes.Equal(message, shipInit) { - text = "ship init" - } else { - text = "unknown single byte" - } - logging.Log().Trace("Send:", w.remoteSki, text) - - case <-ticker.C: - w.handlePing() - } - } -} - -func (w *websocketConnection) handlePing() { - if w.isConnClosed() { - return - } - - w.muxConWrite.Lock() - _ = w.conn.SetWriteDeadline(time.Now().Add(writeWait)) - w.muxConWrite.Unlock() - if err := w.writeMessage(websocket.PingMessage, nil); err != nil { - w.closeWithError(err, "error writing to websocket: ") - return - } -} - -func (w *websocketConnection) closeWithError(err error, reason string) { - logging.Log().Debug(w.remoteSki, reason, err) - w.setConnClosedError(err) - w.dataProcessing.ReportConnectionError(err) -} - -// readShipPump checks for messages from the websocket connection -func (w *websocketConnection) readShipPump() { - _ = w.conn.SetReadDeadline(time.Now().Add(pongWait)) - w.conn.SetPongHandler(func(string) error { _ = w.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) - - for { - if w.isConnClosed() { - return - } - - message, err := w.readWebsocketMessage() - // ignore read errors if the connection got closed - if w.isConnClosed() { - return - } - - if err != nil { - logging.Log().Debug(w.remoteSki, "websocket read error: ", err) - w.close() - w.setConnClosedError(err) - w.dataProcessing.ReportConnectionError(err) - return - } - - var text string - if len(message) > 2 { - text = string(message[1:]) - } else if bytes.Equal(message, shipInit) { - text = "ship init" - } else { - text = "unknown single byte" - } - logging.Log().Trace("Recv:", w.remoteSki, text) - - w.dataProcessing.HandleIncomingShipMessage(message) - } -} - -// read a message from the websocket connection -func (w *websocketConnection) readWebsocketMessage() ([]byte, error) { - if w.conn == nil { - return nil, errors.New("connection is not initialized") - } - - msgType, b, err := w.conn.ReadMessage() - if err != nil { - return nil, err - } - - if msgType != websocket.BinaryMessage { - return nil, errors.New("message is not a binary message") - } - - if len(b) < 2 { - return nil, fmt.Errorf("invalid ship message length") - } - - return b, nil -} - -// close the current websocket connection -func (w *websocketConnection) close() { - w.shutdownOnce.Do(func() { - if w.isConnClosed() { - return - } - - w.setConnClosedError(nil) - - w.muxShipWrite.Lock() - - if !util.IsChannelClosed(w.closeChannel) { - close(w.closeChannel) - w.closeChannel = nil - } - - if !util.IsChannelClosed(w.shipWriteChannel) { - close(w.shipWriteChannel) - w.shipWriteChannel = nil - } - - if w.conn != nil { - w.conn.Close() - } - - w.muxShipWrite.Unlock() - }) -} - -var _ WebsocketDataConnection = (*websocketConnection)(nil) - -func (w *websocketConnection) InitDataProcessing(dataProcessing WebsocketDataProcessing) { - w.dataProcessing = dataProcessing - - w.run() -} - -// write a message to the websocket connection -func (w *websocketConnection) WriteMessageToDataConnection(message []byte) error { - if w.isConnClosed() { - return errors.New("connection is closed") - } - - w.muxShipWrite.Lock() - defer w.muxShipWrite.Unlock() - - if w.conn == nil || w.shipWriteChannel == nil { - return errors.New("connection is closed") - } - - w.shipWriteChannel <- message - return nil -} - -// make sure websocket Write is only called once at a time -func (w *websocketConnection) writeMessage(messageType int, data []byte) error { - w.muxConWrite.Lock() - defer w.muxConWrite.Unlock() - - return w.conn.WriteMessage(messageType, data) -} - -// shutdown the connection and all internals -func (w *websocketConnection) CloseDataConnection(closeCode int, reason string) { - if !w.isConnClosed() { - if reason != "" { - _ = w.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, reason)) - } - w.setConnClosedError(nil) - w.close() - } -} - -// return if the connection is closed -func (w *websocketConnection) IsDataConnectionClosed() (bool, error) { - isClosed := w.isConnClosed() - err := w.connClosedError() - - if isClosed && err == nil { - err = errors.New("connection is closed") - } - - return isClosed, err -} diff --git a/ship/websocket_test.go b/ship/websocket_test.go deleted file mode 100644 index 86f70ef2..00000000 --- a/ship/websocket_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package ship - -import ( - "errors" - "log" - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" - - util "github.com/enbility/eebus-go/util" - "github.com/gorilla/websocket" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "go.uber.org/mock/gomock" -) - -func TestWebsocketSuite(t *testing.T) { - suite.Run(t, new(WebsocketSuite)) -} - -type WebsocketSuite struct { - suite.Suite - - sut *websocketConnection - - testServer *httptest.Server - testWsConn *websocket.Conn - - shipDataProcessing *MockWebsocketDataProcessing -} - -func (s *WebsocketSuite) SetupSuite() {} -func (s *WebsocketSuite) TearDownTest() {} - -func (s *WebsocketSuite) BeforeTest(suiteName, testName string) { - ctrl := gomock.NewController(s.T()) - - s.shipDataProcessing = NewMockWebsocketDataProcessing(ctrl) - s.shipDataProcessing.EXPECT().ReportConnectionError(gomock.Any()).AnyTimes() - s.shipDataProcessing.EXPECT().HandleIncomingShipMessage(gomock.Any()).AnyTimes() - - ts := &testServer{} - s.testServer, s.testWsConn = newWSServer(s.T(), ts) - - s.sut = NewWebsocketConnection(s.testWsConn, "remoteSki") - s.sut.InitDataProcessing(s.shipDataProcessing) -} - -func (s *WebsocketSuite) AfterTest(suiteName, testName string) { - s.testWsConn.Close() - s.testServer.Close() -} - -func (s *WebsocketSuite) TestConnection() { - isClosed := s.sut.isConnClosed() - assert.Equal(s.T(), false, isClosed) - - msg := []byte{0, 0} - err := s.sut.WriteMessageToDataConnection(msg) - assert.Nil(s.T(), err) - - // make sure we have enough time to read and write - time.Sleep(time.Millisecond * 500) - - msg = []byte{1} - msg = append(msg, []byte("message")...) - err = s.sut.WriteMessageToDataConnection(msg) - assert.Nil(s.T(), err) - - // make sure we have enough time to read and write - time.Sleep(time.Millisecond * 500) - - isConnClosed, err := s.sut.IsDataConnectionClosed() - assert.Equal(s.T(), false, isConnClosed) - assert.Nil(s.T(), err) - - s.sut.CloseDataConnection(450, "User Close") - - isConnClosed, err = s.sut.IsDataConnectionClosed() - assert.Equal(s.T(), true, isConnClosed) - assert.NotNil(s.T(), err) - - err = s.sut.WriteMessageToDataConnection(msg) - assert.NotNil(s.T(), err) -} - -func (s *WebsocketSuite) TestConnectionInvalid() { - msg := []byte{100} - err := s.sut.WriteMessageToDataConnection(msg) - assert.Nil(s.T(), err) - - // make sure we have enough time to read and write - time.Sleep(time.Millisecond * 500) - - isConnClosed, err := s.sut.IsDataConnectionClosed() - assert.Equal(s.T(), true, isConnClosed) - assert.NotNil(s.T(), err) -} - -func (s *WebsocketSuite) TestConnectionClose() { - s.sut.close() - - isClosed, err := s.sut.IsDataConnectionClosed() - assert.Equal(s.T(), true, isClosed) - assert.NotNil(s.T(), err) -} - -func (s *WebsocketSuite) TestPingPeriod() { - isClosed, err := s.sut.IsDataConnectionClosed() - assert.Equal(s.T(), false, isClosed) - assert.Nil(s.T(), err) - - if !util.IsRunningOnCI() { - // test if the function is triggered correctly via the timer - time.Sleep(time.Second * 51) - } else { - // speed up the test by running the method directly - s.sut.handlePing() - } - - isClosed, err = s.sut.IsDataConnectionClosed() - assert.Equal(s.T(), false, isClosed) - assert.Nil(s.T(), err) -} - -func (s *WebsocketSuite) TestCloseWithError() { - isClosed, err := s.sut.IsDataConnectionClosed() - assert.Equal(s.T(), false, isClosed) - assert.Nil(s.T(), err) - - err = errors.New("test error") - s.sut.closeWithError(err, "test error") - - isClosed, err = s.sut.IsDataConnectionClosed() - assert.Equal(s.T(), true, isClosed) - assert.NotNil(s.T(), err) -} - -var upgrader = websocket.Upgrader{} - -func newWSServer(t *testing.T, h http.Handler) (*httptest.Server, *websocket.Conn) { - t.Helper() - - s := httptest.NewServer(h) - wsURL := strings.Replace(s.URL, "http://", "ws://", -1) - wsURL = strings.Replace(wsURL, "https://", "wss://", -1) - - ws, _, err := websocket.DefaultDialer.Dial(wsURL, nil) - if err != nil { - t.Fatal(err) - } - - return s, ws -} - -type testServer struct { -} - -func (s *testServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - upgrader.CheckOrigin = func(r *http.Request) bool { return true } - ws, err := upgrader.Upgrade(w, r, nil) - if err != nil { - log.Println("upgrade:", err) - return - } - defer ws.Close() - - for { - _, msg, err := ws.ReadMessage() - if err != nil { - if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { - log.Printf("error: %v", err) - } - return - } - - err = ws.WriteMessage(websocket.BinaryMessage, msg) - if err != nil { - continue - } - } -} diff --git a/spine/api.go b/spine/api.go deleted file mode 100644 index f403cb26..00000000 --- a/spine/api.go +++ /dev/null @@ -1,283 +0,0 @@ -package spine - -import ( - "time" - - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine/model" -) - -type EventHandler interface { - HandleEvent(EventPayload) -} - -/* Device */ - -type Device interface { - Address() *model.AddressDeviceType - DeviceType() *model.DeviceTypeType - FeatureSet() *model.NetworkManagementFeatureSetType - DestinationData() model.NodeManagementDestinationDataType -} - -type DeviceLocal interface { - Device - RemoveRemoteDeviceConnection(ski string) - AddRemoteDeviceForSki(ski string, rDevice DeviceRemote) - SetupRemoteDevice(ski string, writeI ship.SpineDataConnection) ship.SpineDataProcessing - RemoveRemoteDevice(ski string) - RemoteDevices() []DeviceRemote - RemoteDeviceForAddress(address model.AddressDeviceType) DeviceRemote - RemoteDeviceForSki(ski string) DeviceRemote - ProcessCmd(datagram model.DatagramType, remoteDevice DeviceRemote) error - NodeManagement() *NodeManagementImpl - SubscriptionManager() SubscriptionManager - BindingManager() BindingManager - HeartbeatManager() HeartbeatManager - AddEntity(entity EntityLocal) - RemoveEntity(entity EntityLocal) - Entities() []EntityLocal - Entity(id []model.AddressEntityType) EntityLocal - EntityForType(entityType model.EntityTypeType) EntityLocal - FeatureByAddress(address *model.FeatureAddressType) FeatureLocal - NotifySubscribers(featureAddress *model.FeatureAddressType, cmd model.CmdType) - Information() *model.NodeManagementDetailedDiscoveryDeviceInformationType -} - -type DeviceRemote interface { - Device - Ski() string - SetAddress(address *model.AddressDeviceType) - HandleSpineMesssage(message []byte) (*model.MsgCounterType, error) - Sender() Sender - Entity(id []model.AddressEntityType) EntityRemote - Entities() []EntityRemote - FeatureByAddress(address *model.FeatureAddressType) FeatureRemote - RemoveByAddress(addr []model.AddressEntityType) EntityRemote - FeatureByEntityTypeAndRole(entity EntityRemote, featureType model.FeatureTypeType, role model.RoleType) FeatureRemote - UpdateDevice(description *model.NetworkManagementDeviceDescriptionDataType) - AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]EntityRemote, error) - AddEntity(entity EntityRemote) EntityRemote - UseCases() []model.UseCaseInformationDataType - VerifyUseCaseScenariosAndFeaturesSupport( - usecaseActor model.UseCaseActorType, - usecaseName model.UseCaseNameType, - scenarios []model.UseCaseScenarioSupportType, - serverFeatures []model.FeatureTypeType, - ) bool - CheckEntityInformation(initialData bool, entity model.NodeManagementDetailedDiscoveryEntityInformationType) error -} - -/* Entity */ - -type Entity interface { - EntityType() model.EntityTypeType - Address() *model.EntityAddressType - Description() *model.DescriptionType - SetDescription(d *model.DescriptionType) - NextFeatureId() uint -} - -type EntityLocal interface { - Entity - Device() DeviceLocal - AddFeature(f FeatureLocal) - GetOrAddFeature(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal - FeatureOfTypeAndRole(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal - Features() []FeatureLocal - Feature(addressFeature *model.AddressFeatureType) FeatureLocal - Information() *model.NodeManagementDetailedDiscoveryEntityInformationType - AddUseCaseSupport( - actor model.UseCaseActorType, - useCaseName model.UseCaseNameType, - useCaseVersion model.SpecificationVersionType, - useCaseDocumemtSubRevision string, - useCaseAvailable bool, - scenarios []model.UseCaseScenarioSupportType, - ) - RemoveUseCaseSupport( - actor model.UseCaseActorType, - useCaseName model.UseCaseNameType, - ) - RemoveAllUseCaseSupports() - RemoveAllSubscriptions() - RemoveAllBindings() -} - -type EntityRemote interface { - Entity - Device() DeviceRemote - AddFeature(f FeatureRemote) - Features() []FeatureRemote - Feature(addressFeature *model.AddressFeatureType) FeatureRemote - RemoveAllFeatures() -} - -/* Feature */ - -type Feature interface { - Address() *model.FeatureAddressType - Type() model.FeatureTypeType - Role() model.RoleType - Operations() map[model.FunctionType]*Operations - Description() *model.DescriptionType - SetDescription(desc *model.DescriptionType) - SetDescriptionString(s string) - String() string -} - -type FeatureRemote interface { - Feature - DataCopy(function model.FunctionType) any - SetData(function model.FunctionType, data any) - UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType) - Sender() Sender - Device() DeviceRemote - Entity() EntityRemote - SetOperations(functions []model.FunctionPropertyType) - SetMaxResponseDelay(delay *model.MaxResponseDelayType) - MaxResponseDelayDuration() time.Duration -} - -type FeatureLocal interface { - Feature - Device() DeviceLocal - Entity() EntityLocal - DataCopy(function model.FunctionType) any - SetData(function model.FunctionType, data any) - AddResultHandler(handler FeatureResult) - AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg ResultMessage)) - Information() *model.NodeManagementDetailedDiscoveryFeatureInformationType - AddFunctionType(function model.FunctionType, read, write bool) - RequestData( - function model.FunctionType, - selector any, - elements any, - destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) - RequestDataBySenderAddress( - cmd model.CmdType, - sender Sender, - destinationSki string, - destinationAddress *model.FeatureAddressType, - maxDelay time.Duration) (*model.MsgCounterType, *model.ErrorType) - FetchRequestData( - msgCounter model.MsgCounterType, - destination FeatureRemote) (any, *model.ErrorType) - Subscribe(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) - // SubscribeAndWait(remoteDevice DeviceRemote, remoteAdress *model.FeatureAddressType) *ErrorType // Subscribes the local feature to the given destination feature; the go routine will block until the response is processed - RemoveSubscription(remoteAddress *model.FeatureAddressType) - RemoveAllSubscriptions() - Bind(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) - // BindAndWait(remoteDevice DeviceRemote, remoteAddress *model.FeatureAddressType) *ErrorType - RemoveBinding(remoteAddress *model.FeatureAddressType) - RemoveAllBindings() - NotifyData( - function model.FunctionType, - deleteSelector, partialSelector any, - partialWithoutSelector bool, - deleteElements any, - destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) - WriteData( - function model.FunctionType, - deleteSelector, partialSelector any, - deleteElements any, - destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) - HandleMessage(message *Message) *model.ErrorType -} - -type FeatureResult interface { - HandleResult(ResultMessage) -} - -/* Functions */ - -type FunctionDataCmd interface { - FunctionData - ReadCmdType(partialSelector any, elements any) model.CmdType - ReplyCmdType(partial bool) model.CmdType - NotifyCmdType(deleteSelector, partialSelector any, partialWithoutSelector bool, deleteElements any) model.CmdType - WriteCmdType(deleteSelector, partialSelector any, deleteElements any) model.CmdType -} - -type FunctionData interface { - Function() model.FunctionType - DataCopyAny() any - UpdateDataAny(data any, filterPartial *model.FilterType, filterDelete *model.FilterType) -} - -/* Sender */ - -type ComControl interface { - // This must be connected to the correct remote device !! - SendSpineMessage(datagram model.DatagramType) error -} - -//go:generate mockery --name=Sender - -type Sender interface { - // Sends a read cmd to request some data - Request(cmdClassifier model.CmdClassifierType, senderAddress, destinationAddress *model.FeatureAddressType, ackRequest bool, cmd []model.CmdType) (*model.MsgCounterType, error) - // Sends a result cmd with no error to indicate that a message was processed successfully - ResultSuccess(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType) error - // Sends a result cmd with error information to indicate that a message processing failed - ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error - // Sends a reply cmd to response to a read cmd - Reply(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, cmd model.CmdType) error - // Sends a call cmd with a subscription request - Subscribe(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) - // Sends a call cmd with a subscription delete request - Unsubscribe(senderAddress, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) - // Sends a call cmd with a binding request - Bind(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) - // Sends a call cmd with a binding delte request - Unbind(senderAddress, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) - // Sends a notify cmd to indicate that a subscribed feature changed - Notify(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) - // Sends a write cmd, setting properties of remote features - Write(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) - // return the datagram for a given msgCounter (only availbe for Notify messasges!), error if not found - DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) -} - -/* PendingRequests */ - -type PendingRequests interface { - Add(ski string, counter model.MsgCounterType, maxDelay time.Duration) - SetData(ski string, counter model.MsgCounterType, data any) *model.ErrorType - SetResult(ski string, counter model.MsgCounterType, errorResult *model.ErrorType) *model.ErrorType - GetData(ski string, counter model.MsgCounterType) (any, *model.ErrorType) - Remove(ski string, counter model.MsgCounterType) *model.ErrorType -} - -/* Bindings */ - -// implemented by BindingManagerImpl -type BindingManager interface { - AddBinding(remoteDevice DeviceRemote, data model.BindingManagementRequestCallType) error - RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice DeviceRemote) error - RemoveBindingsForDevice(remoteDevice DeviceRemote) - RemoveBindingsForEntity(remoteEntity EntityRemote) - Bindings(remoteDevice DeviceRemote) []*BindingEntry - BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry -} - -/* Subscription Manager */ - -type SubscriptionManager interface { - AddSubscription(remoteDevice DeviceRemote, data model.SubscriptionManagementRequestCallType) error - RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice DeviceRemote) error - RemoveSubscriptionsForDevice(remoteDevice DeviceRemote) - RemoveSubscriptionsForEntity(remoteEntity EntityRemote) - Subscriptions(remoteDevice DeviceRemote) []*SubscriptionEntry - SubscriptionsOnFeature(featureAddress model.FeatureAddressType) []*SubscriptionEntry -} - -/* Heartbeats */ - -type HeartbeatManager interface { - IsHeartbeatRunning() bool - UpdateHeartbeatOnSubscriptions() - SetLocalFeature(entity *EntityLocalImpl, feature FeatureLocal) - StartHeartbeat() error - StopHeartbeat() -} diff --git a/spine/binding_manager.go b/spine/binding_manager.go deleted file mode 100644 index 0b1cac45..00000000 --- a/spine/binding_manager.go +++ /dev/null @@ -1,226 +0,0 @@ -package spine - -import ( - "errors" - "fmt" - "reflect" - "sync" - "sync/atomic" - - "github.com/ahmetb/go-linq/v3" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -type BindingEntry struct { - id uint64 - serverFeature FeatureLocal - clientFeature FeatureRemote -} - -type BindingManagerImpl struct { - localDevice DeviceLocal - - bindingNum uint64 - bindingEntries []*BindingEntry - - mux sync.Mutex - // TODO: add persistence -} - -func NewBindingManager(localDevice DeviceLocal) *BindingManagerImpl { - c := &BindingManagerImpl{ - bindingNum: 0, - localDevice: localDevice, - } - - return c -} - -// is sent from the client (remote device) to the server (local device) -func (c *BindingManagerImpl) AddBinding(remoteDevice DeviceRemote, data model.BindingManagementRequestCallType) error { - - serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) - if serverFeature == nil { - return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *c.localDevice.Address()) - } - if err := c.checkRoleAndType(serverFeature, model.RoleTypeServer, *data.ServerFeatureType); err != nil { - return err - } - - clientFeature := remoteDevice.FeatureByAddress(data.ClientAddress) - if clientFeature == nil { - return fmt.Errorf("client feature '%s' in remote device '%s' not found", data.ClientAddress, *remoteDevice.Address()) - } - if err := c.checkRoleAndType(clientFeature, model.RoleTypeClient, *data.ServerFeatureType); err != nil { - return err - } - - bindingEntry := &BindingEntry{ - id: c.bindingId(), - serverFeature: serverFeature, - clientFeature: clientFeature, - } - - c.mux.Lock() - defer c.mux.Unlock() - - for _, item := range c.bindingEntries { - if reflect.DeepEqual(item.serverFeature, serverFeature) && reflect.DeepEqual(item.clientFeature, clientFeature) { - return fmt.Errorf("requested binding is already present") - } - } - - c.bindingEntries = append(c.bindingEntries, bindingEntry) - - payload := EventPayload{ - Ski: remoteDevice.Ski(), - EventType: EventTypeBindingChange, - ChangeType: ElementChangeAdd, - Data: data, - Feature: clientFeature, - } - Events.Publish(payload) - - return nil -} - -func (c *BindingManagerImpl) RemoveBinding(data model.BindingManagementDeleteCallType, remoteDevice DeviceRemote) error { - var newBindingEntries []*BindingEntry - - // according to the spec 7.4.4 - // a. The absence of "bindingDelete. clientAddress. device" SHALL be treated as if it was - // present and set to the sender's "device" address part. - // b. The absence of "bindingDelete. serverAddress. device" SHALL be treated as if it was - // present and set to the recipient's "device" address part. - - var clientAddress model.FeatureAddressType - util.DeepCopy(data.ClientAddress, &clientAddress) - if data.ClientAddress.Device == nil { - clientAddress.Device = remoteDevice.Address() - } - - clientFeature := remoteDevice.FeatureByAddress(data.ClientAddress) - if clientFeature == nil { - return fmt.Errorf("client feature '%s' in remote device '%s' not found", data.ClientAddress, *remoteDevice.Address()) - } - - serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) - if serverFeature == nil { - return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *c.localDevice.Address()) - } - - c.mux.Lock() - defer c.mux.Unlock() - - for _, item := range c.bindingEntries { - itemAddress := item.clientFeature.Address() - - if !reflect.DeepEqual(*itemAddress, clientAddress) && - !reflect.DeepEqual(item.serverFeature, serverFeature) { - newBindingEntries = append(newBindingEntries, item) - } - } - - if len(newBindingEntries) == len(c.bindingEntries) { - return errors.New("could not find requested BindingId to be removed") - } - - c.bindingEntries = newBindingEntries - - payload := EventPayload{ - Ski: remoteDevice.Ski(), - EventType: EventTypeBindingChange, - ChangeType: ElementChangeRemove, - Data: data, - Device: remoteDevice, - Feature: clientFeature, - } - Events.Publish(payload) - - return nil -} - -// Remove all existing bindings for a given remote device -func (c *BindingManagerImpl) RemoveBindingsForDevice(remoteDevice DeviceRemote) { - if remoteDevice == nil { - return - } - - for _, entity := range remoteDevice.Entities() { - c.RemoveBindingsForEntity(entity) - } -} - -// Remove all existing bindings for a given remote device entity -func (c *BindingManagerImpl) RemoveBindingsForEntity(remoteEntity EntityRemote) { - if remoteEntity == nil { - return - } - - c.mux.Lock() - defer c.mux.Unlock() - - var newBindingEntries []*BindingEntry - for _, item := range c.bindingEntries { - if !reflect.DeepEqual(item.clientFeature.Address().Entity, remoteEntity.Address().Entity) { - newBindingEntries = append(newBindingEntries, item) - continue - } - - clientFeature := remoteEntity.Feature(item.clientFeature.Address().Feature) - payload := EventPayload{ - Ski: remoteEntity.Device().Ski(), - EventType: EventTypeBindingChange, - ChangeType: ElementChangeRemove, - Entity: remoteEntity, - Feature: clientFeature, - } - Events.Publish(payload) - } - - c.bindingEntries = newBindingEntries -} - -func (c *BindingManagerImpl) Bindings(remoteDevice DeviceRemote) []*BindingEntry { - var result []*BindingEntry - - c.mux.Lock() - defer c.mux.Unlock() - - linq.From(c.bindingEntries).WhereT(func(s *BindingEntry) bool { - return s.clientFeature.Device().Ski() == remoteDevice.Ski() - }).ToSlice(&result) - - return result -} - -func (c *BindingManagerImpl) BindingsOnFeature(featureAddress model.FeatureAddressType) []*BindingEntry { - var result []*BindingEntry - - c.mux.Lock() - defer c.mux.Unlock() - - linq.From(c.bindingEntries).WhereT(func(s *BindingEntry) bool { - return reflect.DeepEqual(*s.serverFeature.Address(), featureAddress) - }).ToSlice(&result) - - return result -} - -func (c *BindingManagerImpl) bindingId() uint64 { - i := atomic.AddUint64(&c.bindingNum, 1) - return i -} - -func (c *BindingManagerImpl) checkRoleAndType(feature Feature, role model.RoleType, featureType model.FeatureTypeType) error { - if feature.Role() != model.RoleTypeSpecial && feature.Role() != role { - return fmt.Errorf("found feature %s is not matching required role %s", feature.Type(), role) - } - - if feature.Type() != featureType && feature.Type() != model.FeatureTypeTypeGeneric { - return fmt.Errorf("found feature %s is not matching required type %s", feature.Type(), featureType) - } - - return nil -} diff --git a/spine/binding_manager_test.go b/spine/binding_manager_test.go deleted file mode 100644 index 77557e93..00000000 --- a/spine/binding_manager_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestBindingManagerSuite(t *testing.T) { - suite.Run(t, new(BindingManagerSuite)) -} - -type BindingManagerSuite struct { - suite.Suite - - localDevice DeviceLocal - writeHandler *WriteMessageHandler - remoteDevice DeviceRemote - - sut BindingManager -} - -func (s *BindingManagerSuite) BeforeTest(suiteName, testName string) { - s.localDevice = NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - remoteSki := "TestRemoteSki" - - s.writeHandler = &WriteMessageHandler{} - - sender := NewSender(s.writeHandler) - s.remoteDevice = NewDeviceRemoteImpl(s.localDevice, remoteSki, sender) - - s.sut = NewBindingManager(s.localDevice) -} - -func (suite *BindingManagerSuite) Test_Bindings() { - entity := NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) - suite.localDevice.AddEntity(entity) - - localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - - remoteEntity := NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) - suite.remoteDevice.AddEntity(remoteEntity) - - remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - remoteFeature.Address().Device = util.Ptr(model.AddressDeviceType("remoteDevice")) - remoteEntity.AddFeature(remoteFeature) - - bindingRequest := model.BindingManagementRequestCallType{ - ClientAddress: remoteFeature.Address(), - ServerAddress: localFeature.Address(), - ServerFeatureType: util.Ptr(model.FeatureTypeTypeDeviceDiagnosis), - } - - bindingMgr := suite.localDevice.BindingManager() - err := bindingMgr.AddBinding(suite.remoteDevice, bindingRequest) - assert.Nil(suite.T(), err) - - subs := bindingMgr.Bindings(suite.remoteDevice) - assert.Equal(suite.T(), 1, len(subs)) - - err = bindingMgr.AddBinding(suite.remoteDevice, bindingRequest) - assert.NotNil(suite.T(), err) - - subs = bindingMgr.Bindings(suite.remoteDevice) - assert.Equal(suite.T(), 1, len(subs)) - - address := model.FeatureAddressType{ - Device: entity.Device().Address(), - Entity: entity.Address().Entity, - Feature: util.Ptr(model.AddressFeatureType(10)), - } - entries := bindingMgr.BindingsOnFeature(address) - assert.Equal(suite.T(), 0, len(entries)) - - address.Feature = localFeature.Address().Feature - entries = bindingMgr.BindingsOnFeature(address) - assert.Equal(suite.T(), 1, len(entries)) - - bindingDelete := model.BindingManagementDeleteCallType{ - ClientAddress: remoteFeature.Address(), - ServerAddress: localFeature.Address(), - } - - err = bindingMgr.RemoveBinding(bindingDelete, suite.remoteDevice) - assert.Nil(suite.T(), err) - - subs = bindingMgr.Bindings(suite.remoteDevice) - assert.Equal(suite.T(), 0, len(subs)) - - err = bindingMgr.RemoveBinding(bindingDelete, suite.remoteDevice) - assert.NotNil(suite.T(), err) - - err = bindingMgr.AddBinding(suite.remoteDevice, bindingRequest) - assert.Nil(suite.T(), err) - - subs = bindingMgr.Bindings(suite.remoteDevice) - assert.Equal(suite.T(), 1, len(subs)) - - bindingMgr.RemoveBindingsForDevice(suite.remoteDevice) - - subs = bindingMgr.Bindings(suite.remoteDevice) - assert.Equal(suite.T(), 0, len(subs)) -} diff --git a/spine/const.go b/spine/const.go deleted file mode 100644 index 792d3bc9..00000000 --- a/spine/const.go +++ /dev/null @@ -1,5 +0,0 @@ -package spine - -import "github.com/enbility/eebus-go/spine/model" - -var SpecificationVersion model.SpecificationVersionType = "1.3.0" diff --git a/spine/device.go b/spine/device.go deleted file mode 100644 index b4944619..00000000 --- a/spine/device.go +++ /dev/null @@ -1,54 +0,0 @@ -package spine - -import "github.com/enbility/eebus-go/spine/model" - -type DeviceImpl struct { - address *model.AddressDeviceType - dType *model.DeviceTypeType - featureSet *model.NetworkManagementFeatureSetType -} - -// Initialize a new device -// Both values are required for a local device but provided as empty strings for a remote device -// as the address is only provided via detailed discovery response -func NewDeviceImpl(address *model.AddressDeviceType, dType *model.DeviceTypeType, featureSet *model.NetworkManagementFeatureSetType) *DeviceImpl { - deviceImpl := &DeviceImpl{} - - if dType != nil { - deviceImpl.dType = dType - } - - if address != nil { - deviceImpl.address = address - } - - if featureSet != nil { - deviceImpl.featureSet = featureSet - } - - return deviceImpl -} - -func (r *DeviceImpl) Address() *model.AddressDeviceType { - return r.address -} - -func (r *DeviceImpl) DeviceType() *model.DeviceTypeType { - return r.dType -} - -func (r *DeviceImpl) FeatureSet() *model.NetworkManagementFeatureSetType { - return r.featureSet -} - -func (r *DeviceImpl) DestinationData() model.NodeManagementDestinationDataType { - return model.NodeManagementDestinationDataType{ - DeviceDescription: &model.NetworkManagementDeviceDescriptionDataType{ - DeviceAddress: &model.DeviceAddressType{ - Device: r.Address(), - }, - DeviceType: r.DeviceType(), - NetworkFeatureSet: r.FeatureSet(), - }, - } -} diff --git a/spine/device_local.go b/spine/device_local.go deleted file mode 100644 index c8333059..00000000 --- a/spine/device_local.go +++ /dev/null @@ -1,431 +0,0 @@ -package spine - -import ( - "errors" - "fmt" - "reflect" - "sync" - "time" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -var _ DeviceLocal = (*DeviceLocalImpl)(nil) - -type DeviceLocalImpl struct { - *DeviceImpl - entities []EntityLocal - subscriptionManager SubscriptionManager - bindingManager BindingManager - heartbeatManager HeartbeatManager - nodeManagement *NodeManagementImpl - - remoteDevices map[string]DeviceRemote - - brandName string - deviceModel string - deviceCode string - serialNumber string - - mux sync.Mutex -} - -// BrandName is the brand -// DeviceModel is the model -// SerialNumber is the serial number -// DeviceCode is the SHIP id (accessMethods.id) -// DeviceAddress is the SPINE device address -func NewDeviceLocalImpl( - brandName, deviceModel, serialNumber, deviceCode, deviceAddress string, - deviceType model.DeviceTypeType, - featureSet model.NetworkManagementFeatureSetType, - heartbeatTimeout time.Duration) *DeviceLocalImpl { - address := model.AddressDeviceType(deviceAddress) - - var fSet *model.NetworkManagementFeatureSetType - if len(featureSet) != 0 { - fSet = &featureSet - } - - res := &DeviceLocalImpl{ - DeviceImpl: NewDeviceImpl(&address, &deviceType, fSet), - remoteDevices: make(map[string]DeviceRemote), - brandName: brandName, - deviceModel: deviceModel, - serialNumber: serialNumber, - deviceCode: deviceCode, - } - - res.subscriptionManager = NewSubscriptionManager(res) - res.bindingManager = NewBindingManager(res) - res.heartbeatManager = NewHeartbeatManager(res, res.subscriptionManager, heartbeatTimeout) - - res.addDeviceInformation() - return res -} - -func (r *DeviceLocalImpl) RemoveRemoteDeviceConnection(ski string) { - remoteDevice := r.RemoteDeviceForSki(ski) - - r.RemoveRemoteDevice(ski) - - // inform about the disconnection - payload := EventPayload{ - Ski: ski, - EventType: EventTypeDeviceChange, - ChangeType: ElementChangeRemove, - Device: remoteDevice, - } - Events.Publish(payload) -} - -// Helper method used by tests and AddRemoteDevice -func (r *DeviceLocalImpl) AddRemoteDeviceForSki(ski string, rDevice DeviceRemote) { - r.mux.Lock() - r.remoteDevices[ski] = rDevice - r.mux.Unlock() -} - -// Setup a new remote device with a given SKI and triggers SPINE requesting device details -func (r *DeviceLocalImpl) SetupRemoteDevice(ski string, writeI ship.SpineDataConnection) ship.SpineDataProcessing { - sender := NewSender(writeI) - rDevice := NewDeviceRemoteImpl(r, ski, sender) - - r.AddRemoteDeviceForSki(ski, rDevice) - - // Request Detailed Discovery Data - _, _ = r.nodeManagement.RequestDetailedDiscovery(rDevice.ski, rDevice.address, rDevice.sender) - - // TODO: Add error handling - // If the request returned an error, it should be retried until it does not - - // always add subscription, as it checks if it already exists - _ = Events.subscribe(EventHandlerLevelCore, r) - - return rDevice -} - -// React to some specific events -func (r *DeviceLocalImpl) HandleEvent(payload EventPayload) { - // Subscribe to NodeManagment after DetailedDiscovery is received - if payload.EventType != EventTypeDeviceChange || payload.ChangeType != ElementChangeAdd { - return - } - - if payload.Data == nil { - return - } - - if len(payload.Ski) == 0 { - return - } - - if r.RemoteDeviceForSki(payload.Ski) == nil { - return - } - - switch payload.Data.(type) { - case *model.NodeManagementDetailedDiscoveryDataType: - _, _ = r.nodeManagement.Subscribe(payload.Feature.Address()) - - // Request Use Case Data - _, _ = r.nodeManagement.RequestUseCaseData(payload.Device.Ski(), payload.Device.Address(), payload.Device.Sender()) - } -} - -func (r *DeviceLocalImpl) RemoveRemoteDevice(ski string) { - r.mux.Lock() - defer r.mux.Unlock() - - if r.remoteDevices[ski] == nil { - return - } - - // remove all subscriptions for this device - subscriptionMgr := r.SubscriptionManager() - subscriptionMgr.RemoveSubscriptionsForDevice(r.remoteDevices[ski]) - - // make sure Heartbeat Manager is up to date - r.HeartbeatManager().UpdateHeartbeatOnSubscriptions() - - // remove all bindings for this device - bindingMgr := r.BindingManager() - bindingMgr.RemoveBindingsForDevice(r.remoteDevices[ski]) - - delete(r.remoteDevices, ski) - - // only unsubscribe if we don't have any remote devices left - if len(r.remoteDevices) == 0 { - _ = Events.unsubscribe(EventHandlerLevelCore, r) - } -} - -func (r *DeviceLocalImpl) RemoteDevices() []DeviceRemote { - r.mux.Lock() - defer r.mux.Unlock() - - res := make([]DeviceRemote, 0) - for _, rDevice := range r.remoteDevices { - res = append(res, rDevice) - } - - return res -} - -func (r *DeviceLocalImpl) RemoteDeviceForAddress(address model.AddressDeviceType) DeviceRemote { - r.mux.Lock() - defer r.mux.Unlock() - - for _, item := range r.remoteDevices { - if *item.Address() == address { - return item - } - } - - return nil -} - -func (r *DeviceLocalImpl) RemoteDeviceForSki(ski string) DeviceRemote { - r.mux.Lock() - defer r.mux.Unlock() - - return r.remoteDevices[ski] -} - -func (r *DeviceLocalImpl) ProcessCmd(datagram model.DatagramType, remoteDevice DeviceRemote) error { - destAddr := datagram.Header.AddressDestination - localFeature := r.FeatureByAddress(destAddr) - - cmdClassifier := datagram.Header.CmdClassifier - if len(datagram.Payload.Cmd) == 0 { - return errors.New("no payload cmd content available") - } - cmd := datagram.Payload.Cmd[0] - - // TODO check if cmd.Function is the same as the provided cmd value - filterPartial, filterDelete := cmd.ExtractFilter() - - remoteEntity := remoteDevice.Entity(datagram.Header.AddressSource.Entity) - remoteFeature := remoteDevice.FeatureByAddress(datagram.Header.AddressSource) - if remoteFeature == nil { - return fmt.Errorf("invalid remote feature address: '%s'", datagram.Header.AddressSource) - } - - message := &Message{ - RequestHeader: &datagram.Header, - CmdClassifier: *cmdClassifier, - Cmd: cmd, - FilterPartial: filterPartial, - FilterDelete: filterDelete, - FeatureRemote: remoteFeature, - EntityRemote: remoteEntity, - DeviceRemote: remoteDevice, - } - - ackRequest := datagram.Header.AckRequest - - if localFeature == nil { - errorMessage := "invalid feature address" - _ = remoteFeature.Sender().ResultError(message.RequestHeader, destAddr, model.NewErrorType(model.ErrorNumberTypeDestinationUnknown, errorMessage)) - - return errors.New(errorMessage) - } - - lfType := string(localFeature.Type()) - rfType := "" - if remoteFeature != nil { - remoteFeature.Type() - } - - logging.Log().Debug(datagram.PrintMessageOverview(false, lfType, rfType)) - - err := localFeature.HandleMessage(message) - if err != nil { - // TODO: add error description in a useful format - - // Don't send error responses for incoming resulterror messages - if message.CmdClassifier != model.CmdClassifierTypeResult { - _ = remoteFeature.Sender().ResultError(message.RequestHeader, localFeature.Address(), err) - } - - return errors.New(err.String()) - } - if ackRequest != nil && *ackRequest { - _ = remoteFeature.Sender().ResultSuccess(message.RequestHeader, localFeature.Address()) - } - - return nil -} - -func (r *DeviceLocalImpl) NodeManagement() *NodeManagementImpl { - return r.nodeManagement -} - -func (r *DeviceLocalImpl) SubscriptionManager() SubscriptionManager { - return r.subscriptionManager -} - -func (r *DeviceLocalImpl) BindingManager() BindingManager { - return r.bindingManager -} - -func (r *DeviceLocalImpl) HeartbeatManager() HeartbeatManager { - return r.heartbeatManager -} - -func (r *DeviceLocalImpl) AddEntity(entity EntityLocal) { - r.mux.Lock() - defer r.mux.Unlock() - - r.entities = append(r.entities, entity) - - r.notifySubscribersOfEntity(entity, model.NetworkManagementStateChangeTypeAdded) -} - -func (r *DeviceLocalImpl) RemoveEntity(entity EntityLocal) { - entity.RemoveAllUseCaseSupports() - entity.RemoveAllSubscriptions() - entity.RemoveAllBindings() - - r.mux.Lock() - defer r.mux.Unlock() - - var entities []EntityLocal - for _, e := range r.entities { - if e != entity { - entities = append(entities, e) - } - } - - r.entities = entities -} - -func (r *DeviceLocalImpl) Entities() []EntityLocal { - r.mux.Lock() - defer r.mux.Unlock() - - return r.entities -} - -func (r *DeviceLocalImpl) Entity(id []model.AddressEntityType) EntityLocal { - r.mux.Lock() - defer r.mux.Unlock() - - for _, e := range r.entities { - if reflect.DeepEqual(id, e.Address().Entity) { - return e - } - } - return nil -} - -func (r *DeviceLocalImpl) EntityForType(entityType model.EntityTypeType) EntityLocal { - r.mux.Lock() - defer r.mux.Unlock() - - for _, e := range r.entities { - if e.EntityType() == entityType { - return e - } - } - return nil -} - -func (r *DeviceLocalImpl) FeatureByAddress(address *model.FeatureAddressType) FeatureLocal { - entity := r.Entity(address.Entity) - if entity != nil { - return entity.Feature(address.Feature) - } - return nil -} - -func (r *DeviceLocalImpl) Information() *model.NodeManagementDetailedDiscoveryDeviceInformationType { - res := model.NodeManagementDetailedDiscoveryDeviceInformationType{ - Description: &model.NetworkManagementDeviceDescriptionDataType{ - DeviceAddress: &model.DeviceAddressType{ - Device: r.address, - }, - DeviceType: r.dType, - NetworkFeatureSet: r.featureSet, - }, - } - return &res -} - -// send a notify message to all remote devices -func (r *DeviceLocalImpl) NotifyUseCaseData() { - r.mux.Lock() - defer r.mux.Unlock() - - for _, remoteDevice := range r.remoteDevices { - // TODO: add error management - _, _ = r.nodeManagement.NotifyUseCaseData(remoteDevice) - } -} - -func (r *DeviceLocalImpl) NotifySubscribers(featureAddress *model.FeatureAddressType, cmd model.CmdType) { - subscriptions := r.SubscriptionManager().SubscriptionsOnFeature(*featureAddress) - for _, subscription := range subscriptions { - // TODO: error handling - _, _ = subscription.clientFeature.Sender().Notify(subscription.serverFeature.Address(), subscription.clientFeature.Address(), cmd) - } -} - -func (r *DeviceLocalImpl) notifySubscribersOfEntity(entity EntityLocal, state model.NetworkManagementStateChangeType) { - deviceInformation := r.Information() - entityInformation := *entity.Information() - entityInformation.Description.LastStateChange = &state - - var featureInformation []model.NodeManagementDetailedDiscoveryFeatureInformationType - if state == model.NetworkManagementStateChangeTypeAdded { - for _, f := range entity.Features() { - featureInformation = append(featureInformation, *f.Information()) - } - } - - cmd := model.CmdType{ - Function: util.Ptr(model.FunctionTypeNodeManagementDetailedDiscoveryData), - Filter: filterEmptyPartial(), - NodeManagementDetailedDiscoveryData: &model.NodeManagementDetailedDiscoveryDataType{ - SpecificationVersionList: &model.NodeManagementSpecificationVersionListType{ - SpecificationVersion: []model.SpecificationVersionDataType{model.SpecificationVersionDataType(SpecificationVersion)}, - }, - DeviceInformation: deviceInformation, - EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{entityInformation}, - FeatureInformation: featureInformation, - }, - } - - r.NotifySubscribers(r.nodeManagement.Address(), cmd) -} - -func (r *DeviceLocalImpl) addDeviceInformation() { - entityType := model.EntityTypeTypeDeviceInformation - entity := NewEntityLocalImpl(r, entityType, []model.AddressEntityType{model.AddressEntityType(DeviceInformationEntityId)}) - - { - r.nodeManagement = NewNodeManagementImpl(entity.NextFeatureId(), entity) - entity.AddFeature(r.nodeManagement) - } - { - f := NewFeatureLocalImpl(entity.NextFeatureId(), entity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeServer) - - f.AddFunctionType(model.FunctionTypeDeviceClassificationManufacturerData, true, false) - - manufacturerData := &model.DeviceClassificationManufacturerDataType{ - BrandName: util.Ptr(model.DeviceClassificationStringType(r.brandName)), - VendorName: util.Ptr(model.DeviceClassificationStringType(r.brandName)), - DeviceName: util.Ptr(model.DeviceClassificationStringType(r.deviceModel)), - DeviceCode: util.Ptr(model.DeviceClassificationStringType(r.deviceCode)), - SerialNumber: util.Ptr(model.DeviceClassificationStringType(r.serialNumber)), - } - f.SetData(model.FunctionTypeDeviceClassificationManufacturerData, manufacturerData) - - entity.AddFeature(f) - } - - r.entities = append(r.entities, entity) -} diff --git a/spine/device_local_test.go b/spine/device_local_test.go deleted file mode 100644 index 9de7ff30..00000000 --- a/spine/device_local_test.go +++ /dev/null @@ -1,237 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestDeviceLocalSuite(t *testing.T) { - suite.Run(t, new(DeviceLocalTestSuite)) -} - -type DeviceLocalTestSuite struct { - suite.Suite -} - -var _ ship.SpineDataConnection = (*DeviceLocalTestSuite)(nil) - -func (d *DeviceLocalTestSuite) WriteSpineMessage([]byte) {} - -func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() { - sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - - ski := "test" - _ = sut.SetupRemoteDevice(ski, d) - rDevice := sut.RemoteDeviceForSki(ski) - assert.NotNil(d.T(), rDevice) - - sut.RemoveRemoteDeviceConnection(ski) - - rDevice = sut.RemoteDeviceForSki(ski) - assert.Nil(d.T(), rDevice) -} - -func (d *DeviceLocalTestSuite) Test_RemoteDevice() { - sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) - sut.AddEntity(localEntity) - - f := NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = NewFeatureLocalImpl(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) - - ski := "test" - remote := sut.RemoteDeviceForSki(ski) - assert.Nil(d.T(), remote) - - devices := sut.RemoteDevices() - assert.Equal(d.T(), 0, len(devices)) - - _ = sut.SetupRemoteDevice(ski, d) - remote = sut.RemoteDeviceForSki(ski) - assert.NotNil(d.T(), remote) - - devices = sut.RemoteDevices() - assert.Equal(d.T(), 1, len(devices)) - - entities := sut.Entities() - assert.Equal(d.T(), 2, len(entities)) - - entity1 := sut.Entity([]model.AddressEntityType{1}) - assert.NotNil(d.T(), entity1) - - entity2 := sut.Entity([]model.AddressEntityType{2}) - assert.Nil(d.T(), entity2) - - featureAddress := &model.FeatureAddressType{ - Entity: []model.AddressEntityType{1}, - Feature: util.Ptr(model.AddressFeatureType(1)), - } - feature1 := sut.FeatureByAddress(featureAddress) - assert.NotNil(d.T(), feature1) - - feature2 := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - assert.NotNil(d.T(), feature2) - - sut.RemoveEntity(entity1) - entities = sut.Entities() - assert.Equal(d.T(), 1, len(entities)) - - sut.RemoveRemoteDevice(ski) - remote = sut.RemoteDeviceForSki(ski) - assert.Nil(d.T(), remote) -} - -func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() { - sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) - sut.AddEntity(localEntity) - - ski := "test" - _ = sut.SetupRemoteDevice(ski, d) - remote := sut.RemoteDeviceForSki(ski) - assert.NotNil(d.T(), remote) - - datagram := model.DatagramType{ - Header: model.HeaderType{ - AddressSource: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), - }, - AddressDestination: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), - }, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeRead), - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{}, - }, - } - - err := sut.ProcessCmd(datagram, remote) - assert.NotNil(d.T(), err) - - datagram = model.DatagramType{ - Header: model.HeaderType{ - AddressSource: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), - }, - AddressDestination: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), - }, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeRead), - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{ - {}, - }, - }, - } - - err = sut.ProcessCmd(datagram, remote) - assert.NotNil(d.T(), err) -} - -func (d *DeviceLocalTestSuite) Test_ProcessCmd() { - sut := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := NewEntityLocalImpl(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) - sut.AddEntity(localEntity) - - f := NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = NewFeatureLocalImpl(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) - f = NewFeatureLocalImpl(3, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) - localEntity.AddFeature(f) - - ski := "test" - remoteDeviceName := "remote" - _ = sut.SetupRemoteDevice(ski, d) - remote := sut.RemoteDeviceForSki(ski) - assert.NotNil(d.T(), remote) - - detailedData := &model.NodeManagementDetailedDiscoveryDataType{ - DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ - Description: &model.NetworkManagementDeviceDescriptionDataType{ - DeviceAddress: &model.DeviceAddressType{ - Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), - }, - }, - }, - EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1}, - }, - EntityType: util.Ptr(model.EntityTypeTypeEVSE), - }, - }, - }, - FeatureInformation: []model.NodeManagementDetailedDiscoveryFeatureInformationType{ - { - Description: &model.NetworkManagementFeatureDescriptionDataType{ - FeatureAddress: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1}, - Feature: util.Ptr(model.AddressFeatureType(1)), - }, - FeatureType: util.Ptr(model.FeatureTypeTypeElectricalConnection), - Role: util.Ptr(model.RoleTypeServer), - }, - }, - }, - } - _, err := remote.AddEntityAndFeatures(true, detailedData) - assert.Nil(d.T(), err) - - datagram := model.DatagramType{ - Header: model.HeaderType{ - AddressSource: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1}, - Feature: util.Ptr(model.AddressFeatureType(1)), - }, - AddressDestination: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType("localdevice")), - Entity: []model.AddressEntityType{1}, - }, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeRead), - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{}, - }, - } - - err = sut.ProcessCmd(datagram, remote) - assert.NotNil(d.T(), err) - - cmd := model.CmdType{ - ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{}, - } - - datagram.Payload.Cmd = append(datagram.Payload.Cmd, cmd) - - err = sut.ProcessCmd(datagram, remote) - assert.NotNil(d.T(), err) - - datagram.Header.AddressDestination.Feature = util.Ptr(model.AddressFeatureType(1)) - - err = sut.ProcessCmd(datagram, remote) - assert.NotNil(d.T(), err) - - datagram.Header.AddressDestination.Feature = util.Ptr(model.AddressFeatureType(3)) - - err = sut.ProcessCmd(datagram, remote) - assert.Nil(d.T(), err) -} diff --git a/spine/device_remote.go b/spine/device_remote.go deleted file mode 100644 index f32fe3a2..00000000 --- a/spine/device_remote.go +++ /dev/null @@ -1,360 +0,0 @@ -package spine - -import ( - "encoding/json" - "errors" - "reflect" - "slices" - "sync" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine/model" -) - -var _ DeviceRemote = (*DeviceRemoteImpl)(nil) - -type DeviceRemoteImpl struct { - *DeviceImpl - - ski string - - entities []EntityRemote - entitiesMutex sync.Mutex - - sender Sender - - localDevice DeviceLocal -} - -var _ ship.SpineDataProcessing = (*DeviceRemoteImpl)(nil) - -func NewDeviceRemoteImpl(localDevice DeviceLocal, ski string, sender Sender) *DeviceRemoteImpl { - res := DeviceRemoteImpl{ - DeviceImpl: NewDeviceImpl(nil, nil, nil), - ski: ski, - localDevice: localDevice, - sender: sender, - } - res.addNodeManagement() - - return &res -} - -// return the device SKI -func (d *DeviceRemoteImpl) Ski() string { - return d.ski -} - -func (d *DeviceRemoteImpl) SetAddress(address *model.AddressDeviceType) { - d.address = address -} - -func (d *DeviceRemoteImpl) HandleSpineMesssage(message []byte) (*model.MsgCounterType, error) { - datagram := model.Datagram{} - if err := json.Unmarshal([]byte(message), &datagram); err != nil { - return nil, err - } - err := d.localDevice.ProcessCmd(datagram.Datagram, d) - if err != nil { - logging.Log().Trace(err) - } - - return datagram.Datagram.Header.MsgCounter, nil -} - -// processing incoming SPINE message from the associated SHIP connection -func (d *DeviceRemoteImpl) HandleIncomingSpineMesssage(message []byte) { - _, _ = d.HandleSpineMesssage(message) -} - -func (d *DeviceRemoteImpl) addNodeManagement() { - deviceInformation := d.addNewEntity(model.EntityTypeTypeDeviceInformation, NewAddressEntityType([]uint{DeviceInformationEntityId})) - nodeManagement := NewFeatureRemoteImpl(deviceInformation.NextFeatureId(), deviceInformation, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) - deviceInformation.AddFeature(nodeManagement) -} - -func (d *DeviceRemoteImpl) Sender() Sender { - return d.sender -} - -// Return an entity with a given address -func (d *DeviceRemoteImpl) Entity(id []model.AddressEntityType) EntityRemote { - d.entitiesMutex.Lock() - defer d.entitiesMutex.Unlock() - - for _, e := range d.entities { - if reflect.DeepEqual(id, e.Address().Entity) { - return e - } - } - return nil -} - -// Return all entities of this device -func (d *DeviceRemoteImpl) Entities() []EntityRemote { - return d.entities -} - -// Return the feature for a given address -func (d *DeviceRemoteImpl) FeatureByAddress(address *model.FeatureAddressType) FeatureRemote { - entity := d.Entity(address.Entity) - if entity != nil { - return entity.Feature(address.Feature) - } - return nil -} - -// Remove an entity with a given address from this device -func (d *DeviceRemoteImpl) RemoveByAddress(addr []model.AddressEntityType) EntityRemote { - entityForRemoval := d.Entity(addr) - if entityForRemoval == nil { - return nil - } - - d.entitiesMutex.Lock() - defer d.entitiesMutex.Unlock() - - var newEntities []EntityRemote - for _, item := range d.entities { - if !reflect.DeepEqual(item, entityForRemoval) { - newEntities = append(newEntities, item) - } - } - d.entities = newEntities - - return entityForRemoval -} - -// Get the feature for a given entity, feature type and feature role -func (r *DeviceRemoteImpl) FeatureByEntityTypeAndRole(entity EntityRemote, featureType model.FeatureTypeType, role model.RoleType) FeatureRemote { - if len(r.entities) < 1 { - return nil - } - - r.entitiesMutex.Lock() - defer r.entitiesMutex.Unlock() - - for _, e := range r.entities { - if entity != e { - continue - } - for _, feature := range entity.Features() { - if feature.Type() == featureType && feature.Role() == role { - return feature - } - } - } - - return nil -} - -func (d *DeviceRemoteImpl) UpdateDevice(description *model.NetworkManagementDeviceDescriptionDataType) { - if description != nil { - if description.DeviceAddress != nil && description.DeviceAddress.Device != nil { - d.address = description.DeviceAddress.Device - } - if description.DeviceType != nil { - d.dType = description.DeviceType - } - if description.NetworkFeatureSet != nil { - d.featureSet = description.NetworkFeatureSet - } - } -} - -func (d *DeviceRemoteImpl) AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]EntityRemote, error) { - rEntites := make([]EntityRemote, 0) - - for _, ei := range data.EntityInformation { - if err := d.CheckEntityInformation(initialData, ei); err != nil { - return nil, err - } - - entityAddress := ei.Description.EntityAddress.Entity - - entity := d.Entity(entityAddress) - if entity == nil { - entity = d.addNewEntity(*ei.Description.EntityType, entityAddress) - rEntites = append(rEntites, entity) - } - - entity.SetDescription(ei.Description.Description) - entity.RemoveAllFeatures() - - for _, fi := range data.FeatureInformation { - if reflect.DeepEqual(fi.Description.FeatureAddress.Entity, entityAddress) { - if f, ok := unmarshalFeature(entity, fi); ok { - entity.AddFeature(f) - } - } - } - - // TOV-TODO: check this approach - // if err := f.announceFeatureDiscovery(entity); err != nil { - // return err - // } - } - - return rEntites, nil -} - -// check if the provided entity information is correct -// provide initialData to check if the entity is new and not an update -func (d *DeviceRemoteImpl) CheckEntityInformation(initialData bool, entity model.NodeManagementDetailedDiscoveryEntityInformationType) error { - description := entity.Description - if description == nil { - return errors.New("nodemanagement.replyDetailedDiscoveryData: invalid EntityInformation.Description") - } - - if description.EntityAddress == nil { - return errors.New("nodemanagement.replyDetailedDiscoveryData: invalid EntityInformation.Description.EntityAddress") - } - - if description.EntityAddress.Entity == nil { - return errors.New("nodemanagement.replyDetailedDiscoveryData: invalid EntityInformation.Description.EntityAddress.Entity") - } - - // Consider on initial NodeManagement Detailed Discovery, the device being empty as it is not yet known - if initialData { - return nil - } - - address := d.Address() - if description.EntityAddress.Device != nil && address != nil && *description.EntityAddress.Device != *address { - return errors.New("nodemanagement.replyDetailedDiscoveryData: device address mismatch") - } - - return nil -} - -func (d *DeviceRemoteImpl) addNewEntity(eType model.EntityTypeType, address []model.AddressEntityType) EntityRemote { - newEntity := NewEntityRemoteImpl(d, eType, address) - return d.AddEntity(newEntity) -} - -func (d *DeviceRemoteImpl) AddEntity(entity EntityRemote) EntityRemote { - d.entitiesMutex.Lock() - defer d.entitiesMutex.Unlock() - - d.entities = append(d.entities, entity) - - return entity -} - -func (d *DeviceRemoteImpl) UseCases() []model.UseCaseInformationDataType { - entity := d.Entity(DeviceInformationAddressEntity) - - nodemgmt := d.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) - - data := nodemgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - if data != nil { - return data.UseCaseInformation - } - - return nil -} - -// 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 { - entity := d.Entity(DeviceInformationAddressEntity) - - nodemgmt := d.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) - - usecases := nodemgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - - if usecases == nil || len(usecases.UseCaseInformation) == 0 { - return false - } - - usecaseAndScenariosFound := false - for _, usecase := range usecases.UseCaseInformation { - if usecase.Actor == nil || *usecase.Actor != usecaseActor { - continue - } - - for _, support := range usecase.UseCaseSupport { - if support.UseCaseName == nil || *support.UseCaseName != usecaseName { - 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 EntityRemote, - featureData model.NodeManagementDetailedDiscoveryFeatureInformationType, -) (FeatureRemote, bool) { - var result FeatureRemote - - fid := featureData.Description - - if fid == nil { - return nil, false - } - - result = NewFeatureRemoteImpl(uint(*fid.FeatureAddress.Feature), entity, *fid.FeatureType, *fid.Role) - - result.SetDescription(fid.Description) - result.SetMaxResponseDelay(fid.MaxResponseDelay) - result.SetOperations(fid.SupportedFunction) - - return result, true -} diff --git a/spine/device_remote_test.go b/spine/device_remote_test.go deleted file mode 100644 index cd860979..00000000 --- a/spine/device_remote_test.go +++ /dev/null @@ -1,275 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -const ( - nm_usecaseinformationlistdata_recv_reply_file_path = "../spine/testdata/nm_usecaseinformationlistdata_recv_reply.json" -) - -func TestDeviceRemoteSuite(t *testing.T) { - suite.Run(t, new(DeviceRemoteSuite)) -} - -type DeviceRemoteSuite struct { - suite.Suite - - localDevice DeviceLocal - remoteDevice DeviceRemote - remoteEntity EntityRemote -} - -func (s *DeviceRemoteSuite) WriteSpineMessage([]byte) {} - -func (s *DeviceRemoteSuite) SetupSuite() {} - -func (s *DeviceRemoteSuite) BeforeTest(suiteName, testName string) { - s.localDevice = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - - ski := "test" - sender := NewSender(s) - s.remoteDevice = NewDeviceRemoteImpl(s.localDevice, ski, sender) - s.remoteDevice.SetAddress(util.Ptr(model.AddressDeviceType("test"))) - _ = s.localDevice.SetupRemoteDevice(ski, s) - - s.remoteEntity = NewEntityRemoteImpl(s.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) - - feature := NewFeatureRemoteImpl(0, s.remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - s.remoteEntity.AddFeature(feature) - - s.remoteDevice.AddEntity(s.remoteEntity) -} - -func (s *DeviceRemoteSuite) Test_RemoveByAddress() { - assert.Equal(s.T(), 2, len(s.remoteDevice.Entities())) - - s.remoteDevice.RemoveByAddress([]model.AddressEntityType{2}) - assert.Equal(s.T(), 2, len(s.remoteDevice.Entities())) - - s.remoteDevice.RemoveByAddress([]model.AddressEntityType{1}) - assert.Equal(s.T(), 1, len(s.remoteDevice.Entities())) -} - -func (s *DeviceRemoteSuite) Test_FeatureByEntityTypeAndRole() { - entity := s.remoteDevice.Entity([]model.AddressEntityType{1}) - assert.NotNil(s.T(), entity) - - assert.Equal(s.T(), 1, len(entity.Features())) - - feature := s.remoteDevice.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - assert.Nil(s.T(), feature) - - feature = s.remoteDevice.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - assert.NotNil(s.T(), feature) - - s.remoteDevice.RemoveByAddress([]model.AddressEntityType{1}) - assert.Equal(s.T(), 1, len(s.remoteDevice.Entities())) - - _ = s.remoteDevice.Entity([]model.AddressEntityType{0}) - s.remoteDevice.RemoveByAddress([]model.AddressEntityType{0}) - assert.Equal(s.T(), 0, len(s.remoteDevice.Entities())) - - feature = s.remoteDevice.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - assert.Nil(s.T(), feature) -} - -func (s *DeviceRemoteSuite) Test_Usecases() { - uc := s.remoteDevice.UseCases() - assert.Nil(s.T(), uc) - - _, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) - - uc = s.remoteDevice.UseCases() - assert.NotNil(s.T(), uc) -} - -func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport_ElliJSON() { - _, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_usecaseinformationlistdata_recv_reply_file_path)) - - result := s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeBatterySystem, - model.UseCaseNameTypeControlOfBattery, - []model.UseCaseScenarioSupportType{}, - []model.FeatureTypeType{}, - ) - assert.Equal(s.T(), false, result) - - result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - []model.UseCaseScenarioSupportType{}, - []model.FeatureTypeType{}, - ) - assert.Equal(s.T(), true, result) -} - -func (s *DeviceRemoteSuite) Test_VerifyUseCaseScenariosAndFeaturesSupport() { - result := s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - []model.UseCaseScenarioSupportType{}, - []model.FeatureTypeType{}, - ) - assert.Equal(s.T(), false, result) - - nodeMgmtEntity := s.remoteDevice.Entity(DeviceInformationAddressEntity) - nodeMgmt := nodeMgmtEntity.Feature(util.Ptr(model.AddressFeatureType(NodeManagementFeatureId))) - - // initialize with empty data - newData := &model.NodeManagementUseCaseDataType{ - UseCaseInformation: []model.UseCaseInformationDataType{}, - } - nodeMgmt.UpdateData(model.FunctionTypeNodeManagementUseCaseData, newData, nil, nil) - - data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - - address := model.FeatureAddressType{ - Device: s.remoteDevice.Address(), - Entity: s.remoteEntity.Address().Entity, - } - - data.AddUseCaseSupport( - address, - model.UseCaseActorTypeBatterySystem, - model.UseCaseNameTypeControlOfBattery, - model.SpecificationVersionType("1.0.0"), - "", - true, - []model.UseCaseScenarioSupportType{1}, - ) - nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) - data = nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - - result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - nil, - nil, - ) - assert.Equal(s.T(), false, result) - - data.AddUseCaseSupport( - address, - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVCommissioningAndConfiguration, - model.SpecificationVersionType("1.0.0"), - "", - true, - []model.UseCaseScenarioSupportType{1}, - ) - nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) - data = nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - - result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - nil, - nil, - ) - assert.Equal(s.T(), false, result) - - data.AddUseCaseSupport( - address, - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - model.SpecificationVersionType("1.0.0"), - "", - false, - []model.UseCaseScenarioSupportType{1}, - ) - nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) - data = nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - - result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - nil, - nil, - ) - assert.Equal(s.T(), true, result) - - data.AddUseCaseSupport( - address, - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - model.SpecificationVersionType("1.0.0"), - "", - true, - []model.UseCaseScenarioSupportType{1}, - ) - nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) - - result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - nil, - nil, - ) - assert.Equal(s.T(), true, result) - - result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - []model.UseCaseScenarioSupportType{2}, - nil, - ) - assert.Equal(s.T(), false, result) - - result = s.remoteDevice.VerifyUseCaseScenariosAndFeaturesSupport( - model.UseCaseActorTypeEVSE, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - []model.UseCaseScenarioSupportType{1}, - nil, - ) - 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 := 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 = 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) -} diff --git a/spine/entity.go b/spine/entity.go deleted file mode 100644 index 8e48b99c..00000000 --- a/spine/entity.go +++ /dev/null @@ -1,86 +0,0 @@ -package spine - -import ( - "github.com/ahmetb/go-linq/v3" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -const DeviceInformationEntityId uint = 0 - -var DeviceInformationAddressEntity = []model.AddressEntityType{model.AddressEntityType(DeviceInformationEntityId)} - -type EntityImpl struct { - eType model.EntityTypeType - address *model.EntityAddressType - description *model.DescriptionType - fIdGenerator func() uint -} - -var _ Entity = (*EntityImpl)(nil) - -func NewEntity(eType model.EntityTypeType, deviceAdress *model.AddressDeviceType, entityAddress []model.AddressEntityType) *EntityImpl { - entity := &EntityImpl{ - eType: eType, - address: &model.EntityAddressType{ - Device: deviceAdress, - Entity: entityAddress, - }, - } - if entityAddress[0] == 0 { - // Entity 0 Feature addresses start with 0 - entity.fIdGenerator = newFeatureIdGenerator(0) - } else { - // Entity 1 and further Feature addresses start with 1 - entity.fIdGenerator = newFeatureIdGenerator(1) - } - - return entity -} - -func (r *EntityImpl) EntityType() model.EntityTypeType { - return r.eType -} - -func (r *EntityImpl) Address() *model.EntityAddressType { - return r.address -} - -func (r *EntityImpl) Description() *model.DescriptionType { - return r.description -} - -func (r *EntityImpl) SetDescription(d *model.DescriptionType) { - r.description = d -} - -func (r *EntityImpl) NextFeatureId() uint { - return r.fIdGenerator() -} - -func EntityAddressType(deviceAdress *model.AddressDeviceType, entityAddress []model.AddressEntityType) *model.EntityAddressType { - return &model.EntityAddressType{ - Device: deviceAdress, - Entity: entityAddress, - } -} - -func NewEntityAddressType(deviceName string, entityIds []uint) *model.EntityAddressType { - return &model.EntityAddressType{ - Device: util.Ptr(model.AddressDeviceType(deviceName)), - Entity: NewAddressEntityType(entityIds), - } -} - -func NewAddressEntityType(entityIds []uint) []model.AddressEntityType { - var addressEntity []model.AddressEntityType - linq.From(entityIds).SelectT(func(i uint) model.AddressEntityType { return model.AddressEntityType(i) }).ToSlice(&addressEntity) - return addressEntity -} - -func newFeatureIdGenerator(id uint) func() uint { - return func() uint { - defer func() { id += 1 }() - return id - } -} diff --git a/spine/entity_local.go b/spine/entity_local.go deleted file mode 100644 index 68143e0a..00000000 --- a/spine/entity_local.go +++ /dev/null @@ -1,164 +0,0 @@ -package spine - -import ( - "github.com/enbility/eebus-go/spine/model" -) - -var _ EntityLocal = (*EntityLocalImpl)(nil) - -type EntityLocalImpl struct { - *EntityImpl - device DeviceLocal - features []FeatureLocal -} - -func NewEntityLocalImpl(device DeviceLocal, eType model.EntityTypeType, entityAddress []model.AddressEntityType) *EntityLocalImpl { - return &EntityLocalImpl{ - EntityImpl: NewEntity(eType, device.Address(), entityAddress), - device: device, - } -} - -func (r *EntityLocalImpl) Device() DeviceLocal { - return r.device -} - -// Add a feature to the entity if it is not already added -func (r *EntityLocalImpl) AddFeature(f FeatureLocal) { - // check if this feature is already added - for _, f2 := range r.features { - if f2.Type() == f.Type() && f2.Role() == f.Role() { - return - } - } - r.features = append(r.features, f) -} - -// either returns an existing feature or creates a new one -// for a given entity, featuretype and role -func (r *EntityLocalImpl) GetOrAddFeature(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal { - if f := r.FeatureOfTypeAndRole(featureType, role); f != nil { - return f - } - f := NewFeatureLocalImpl(r.NextFeatureId(), r, featureType, role) - - description := string(featureType) - switch role { - case model.RoleTypeClient: - description += " Client" - case model.RoleTypeServer: - description += " Server" - } - f.SetDescriptionString(description) - r.features = append(r.features, f) - - if role == model.RoleTypeServer && featureType == model.FeatureTypeTypeDeviceDiagnosis { - // Update HeartbeatManagerImpl - r.device.HeartbeatManager().SetLocalFeature(r, f) - } - - return f -} - -func (r *EntityLocalImpl) FeatureOfTypeAndRole(featureType model.FeatureTypeType, role model.RoleType) FeatureLocal { - for _, f := range r.features { - if f.Type() == featureType && f.Role() == role { - return f - } - } - return nil -} - -func (r *EntityLocalImpl) Features() []FeatureLocal { - return r.features -} - -func (r *EntityLocalImpl) Feature(addressFeature *model.AddressFeatureType) FeatureLocal { - if addressFeature == nil { - return nil - } - for _, f := range r.features { - if *f.Address().Feature == *addressFeature { - return f - } - } - return nil -} - -func (r *EntityLocalImpl) Information() *model.NodeManagementDetailedDiscoveryEntityInformationType { - res := &model.NodeManagementDetailedDiscoveryEntityInformationType{ - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: r.Address(), - EntityType: &r.eType, - }, - } - - return res -} - -// add a new usecase -func (r *EntityLocalImpl) AddUseCaseSupport( - actor model.UseCaseActorType, - useCaseName model.UseCaseNameType, - useCaseVersion model.SpecificationVersionType, - useCaseDocumemtSubRevision string, - useCaseAvailable bool, - scenarios []model.UseCaseScenarioSupportType, -) { - nodeMgmt := r.device.NodeManagement() - - data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - if data == nil { - data = &model.NodeManagementUseCaseDataType{} - } - - address := model.FeatureAddressType{ - Device: r.address.Device, - Entity: r.address.Entity, - } - - data.AddUseCaseSupport(address, actor, useCaseName, useCaseVersion, useCaseDocumemtSubRevision, useCaseAvailable, scenarios) - - nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) -} - -// Remove a usecase with a given actor ans usecase name -func (r *EntityLocalImpl) RemoveUseCaseSupport( - actor model.UseCaseActorType, - useCaseName model.UseCaseNameType, -) { - nodeMgmt := r.device.NodeManagement() - - data := nodeMgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) - if data == nil { - return - } - - address := model.FeatureAddressType{ - Device: r.address.Device, - Entity: r.address.Entity, - } - - data.RemoveUseCaseSupport(address, actor, useCaseName) - - nodeMgmt.SetData(model.FunctionTypeNodeManagementUseCaseData, data) -} - -// Remove all usecases -func (r *EntityLocalImpl) RemoveAllUseCaseSupports() { - r.RemoveUseCaseSupport("", "") -} - -// Remove all subscriptions -func (r *EntityLocalImpl) RemoveAllSubscriptions() { - for _, item := range r.features { - item.RemoveAllSubscriptions() - } -} - -// Remove all bindings -func (r *EntityLocalImpl) RemoveAllBindings() { - for _, item := range r.features { - item.RemoveAllBindings() - } -} diff --git a/spine/entity_local_test.go b/spine/entity_local_test.go deleted file mode 100644 index ae680d01..00000000 --- a/spine/entity_local_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestEntityLocalSuite(t *testing.T) { - suite.Run(t, new(EntityLocalTestSuite)) -} - -type EntityLocalTestSuite struct { - suite.Suite -} - -func (suite *EntityLocalTestSuite) Test_Entity() { - device := NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - entity := NewEntityLocalImpl(device, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1})) - device.AddEntity(entity) - - f := NewFeatureLocalImpl(1, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - entity.AddFeature(f) - assert.Equal(suite.T(), 1, len(entity.Features())) - - entity.AddFeature(f) - assert.Equal(suite.T(), 1, len(entity.Features())) - - f1 := entity.Feature(nil) - assert.Nil(suite.T(), f1) - - f1 = entity.Feature(f.Address().Feature) - assert.NotNil(suite.T(), f1) - - fakeAddress := model.AddressFeatureType(5) - f1 = entity.Feature(&fakeAddress) - assert.Nil(suite.T(), f1) - - f2 := entity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - assert.NotNil(suite.T(), f2) - - assert.Equal(suite.T(), 2, len(entity.Features())) - - f3 := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - assert.NotNil(suite.T(), f3) - - assert.Equal(suite.T(), 3, len(entity.Features())) - - f4 := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - assert.NotNil(suite.T(), f4) - - assert.Equal(suite.T(), 3, len(entity.Features())) - - entity.RemoveAllUseCaseSupports() - - entity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - model.SpecificationVersionType("1.0.0"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2}, - ) - - entity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - model.SpecificationVersionType("1.0.0"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2}, - ) - - entity.RemoveAllUseCaseSupports() - entity.RemoveAllBindings() - entity.RemoveAllSubscriptions() -} diff --git a/spine/entity_remote.go b/spine/entity_remote.go deleted file mode 100644 index 9b9bb9e5..00000000 --- a/spine/entity_remote.go +++ /dev/null @@ -1,45 +0,0 @@ -package spine - -import ( - "github.com/enbility/eebus-go/spine/model" -) - -var _ EntityRemote = (*EntityRemoteImpl)(nil) - -type EntityRemoteImpl struct { - *EntityImpl - device DeviceRemote - features []FeatureRemote -} - -func NewEntityRemoteImpl(device DeviceRemote, eType model.EntityTypeType, entityAddress []model.AddressEntityType) *EntityRemoteImpl { - return &EntityRemoteImpl{ - EntityImpl: NewEntity(eType, device.Address(), entityAddress), - device: device, - } -} - -func (r *EntityRemoteImpl) Device() DeviceRemote { - return r.device -} - -func (r *EntityRemoteImpl) AddFeature(f FeatureRemote) { - r.features = append(r.features, f) -} - -func (r *EntityRemoteImpl) Features() []FeatureRemote { - return r.features -} - -func (r *EntityRemoteImpl) Feature(addressFeature *model.AddressFeatureType) FeatureRemote { - for _, f := range r.features { - if *f.Address().Feature == *addressFeature { - return f - } - } - return nil -} - -func (r *EntityRemoteImpl) RemoveAllFeatures() { - r.features = nil -} diff --git a/spine/events.go b/spine/events.go deleted file mode 100644 index 53ac0f4c..00000000 --- a/spine/events.go +++ /dev/null @@ -1,141 +0,0 @@ -package spine - -import ( - "sync" - - "github.com/enbility/eebus-go/spine/model" -) - -var Events events - -type EventHandlerLevel uint - -const ( - EventHandlerLevelCore EventHandlerLevel = iota // Shall only be used by the core stack - EventHandlerLevelApplication // Shall only be used by applications -) - -type ElementChangeType uint16 - -const ( - ElementChangeAdd ElementChangeType = iota - ElementChangeUpdate - ElementChangeRemove -) - -type EventType uint16 - -const ( - EventTypeDeviceChange EventType = iota // Sent after successful response of NodeManagementDetailedDiscovery - EventTypeEntityChange // Sent after successful response of NodeManagementDetailedDiscovery - EventTypeSubscriptionChange // Sent after successful subscription request from remote - EventTypeBindingChange // Sent after successful binding request from remote - EventTypeDataChange // Sent after remote provided new data items for a function -) - -type EventPayload struct { - Ski string // required - EventType EventType // required - ChangeType ElementChangeType // required - Device DeviceRemote // required for DetailedDiscovery Call - Entity EntityRemote // required for DetailedDiscovery Call and Notify - Feature FeatureRemote - CmdClassifier *model.CmdClassifierType // optional, used together with EventType EventTypeDataChange - Data any -} - -type eventHandlerItem struct { - Level EventHandlerLevel - Handler EventHandler -} - -type events struct { - mu sync.Mutex - muHandle sync.Mutex - - handlers []eventHandlerItem // event handling outside of the core stack -} - -// will be used in EEBUS core directly to access the level EventHandlerLevelCore -func (r *events) subscribe(level EventHandlerLevel, handler EventHandler) error { - r.mu.Lock() - defer r.mu.Unlock() - - exists := false - for _, item := range r.handlers { - if item.Level == level && item.Handler == handler { - exists = true - break - } - } - - if !exists { - newHandlerItem := eventHandlerItem{ - Level: level, - Handler: handler, - } - r.handlers = append(r.handlers, newHandlerItem) - } - - return nil -} - -// Subscribe to message events and handle them in -// the Eventhandler interface implementation -// -// returns an error if EventHandlerLevelCore is used as -// that is only allowed for internal use -func (r *events) Subscribe(handler EventHandler) error { - return r.subscribe(EventHandlerLevelApplication, handler) -} - -// will be used in EEBUS core directly to access the level EventHandlerLevelCore -func (r *events) unsubscribe(level EventHandlerLevel, handler EventHandler) error { - r.mu.Lock() - defer r.mu.Unlock() - - var newHandlers []eventHandlerItem - for _, item := range r.handlers { - if item.Level != level && item.Handler != handler { - newHandlers = append(newHandlers, item) - } - } - - r.handlers = newHandlers - - return nil -} - -// Unsubscribe from getting events -func (r *events) Unsubscribe(handler EventHandler) error { - return r.unsubscribe(EventHandlerLevelApplication, handler) -} - -// Publish an event to all subscribers -func (r *events) Publish(payload EventPayload) { - r.mu.Lock() - var handler []eventHandlerItem - copy(r.handlers, handler) - r.mu.Unlock() - - // Use different locks, so unpublish is possible in the event handlers - r.muHandle.Lock() - // process subscribers by level - handlerLevels := []EventHandlerLevel{ - EventHandlerLevelCore, - EventHandlerLevelApplication, - } - - for _, level := range handlerLevels { - for _, item := range r.handlers { - if item.Level != level { - continue - } - - // do not run this asynchronously, to make sure all required - // and expected actions are taken - item.Handler.HandleEvent(payload) - } - } - r.muHandle.Unlock() -} diff --git a/spine/feature.go b/spine/feature.go deleted file mode 100644 index 7b553df9..00000000 --- a/spine/feature.go +++ /dev/null @@ -1,73 +0,0 @@ -package spine - -import ( - "fmt" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -type FeatureImpl struct { - address *model.FeatureAddressType - ftype model.FeatureTypeType - description *model.DescriptionType - role model.RoleType - operations map[model.FunctionType]*Operations -} - -var _ Feature = (*FeatureImpl)(nil) - -func NewFeatureImpl(address *model.FeatureAddressType, ftype model.FeatureTypeType, role model.RoleType) *FeatureImpl { - res := &FeatureImpl{ - address: address, - ftype: ftype, - role: role, - } - - return res -} - -func (r *FeatureImpl) Address() *model.FeatureAddressType { - return r.address -} - -func (r *FeatureImpl) Type() model.FeatureTypeType { - return r.ftype -} - -func (r *FeatureImpl) Role() model.RoleType { - return r.role -} - -func (r *FeatureImpl) Operations() map[model.FunctionType]*Operations { - return r.operations -} - -func (r *FeatureImpl) Description() *model.DescriptionType { - return r.description -} - -func (r *FeatureImpl) SetDescription(d *model.DescriptionType) { - r.description = d -} - -func (r *FeatureImpl) SetDescriptionString(s string) { - r.description = util.Ptr(model.DescriptionType(s)) -} - -func (r *FeatureImpl) String() string { - if r == nil { - return "" - } - return fmt.Sprintf("Id: %d (%s)", *r.Address().Feature, *r.Description()) -} - -func featureAddressType(id uint, entityAddress *model.EntityAddressType) *model.FeatureAddressType { - res := model.FeatureAddressType{ - Device: entityAddress.Device, - Entity: entityAddress.Entity, - Feature: util.Ptr(model.AddressFeatureType(id)), - } - - return &res -} diff --git a/spine/feature_local.go b/spine/feature_local.go deleted file mode 100644 index 56695312..00000000 --- a/spine/feature_local.go +++ /dev/null @@ -1,465 +0,0 @@ -package spine - -import ( - "fmt" - "reflect" - "sync" - "time" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -var _ FeatureLocal = (*FeatureLocalImpl)(nil) - -type FeatureLocalImpl struct { - *FeatureImpl - - muxResultCB sync.Mutex - entity EntityLocal - functionDataMap map[model.FunctionType]FunctionDataCmd - pendingRequests PendingRequests - resultHandler []FeatureResult - resultCallback map[model.MsgCounterType]func(result ResultMessage) - - bindings []*model.FeatureAddressType - subscriptions []*model.FeatureAddressType - - mux sync.Mutex -} - -func NewFeatureLocalImpl(id uint, entity EntityLocal, ftype model.FeatureTypeType, role model.RoleType) *FeatureLocalImpl { - res := &FeatureLocalImpl{ - FeatureImpl: NewFeatureImpl( - featureAddressType(id, entity.Address()), - ftype, - role), - entity: entity, - functionDataMap: make(map[model.FunctionType]FunctionDataCmd), - pendingRequests: NewPendingRequest(), - resultCallback: make(map[model.MsgCounterType]func(result ResultMessage)), - } - - for _, fd := range CreateFunctionData[FunctionDataCmd](ftype) { - res.functionDataMap[fd.Function()] = fd - } - res.operations = make(map[model.FunctionType]*Operations) - - return res -} - -func (r *FeatureLocalImpl) Device() DeviceLocal { - return r.entity.Device() -} - -func (r *FeatureLocalImpl) Entity() EntityLocal { - return r.entity -} - -// Add supported function to the feature if its role is Server or Special -func (r *FeatureLocalImpl) AddFunctionType(function model.FunctionType, read, write bool) { - if r.role != model.RoleTypeServer && r.role != model.RoleTypeSpecial { - return - } - if r.operations[function] != nil { - return - } - r.operations[function] = NewOperations(read, write) -} - -func (r *FeatureLocalImpl) DataCopy(function model.FunctionType) any { - r.mux.Lock() - defer r.mux.Unlock() - - return r.functionData(function).DataCopyAny() -} - -func (r *FeatureLocalImpl) SetData(function model.FunctionType, data any) { - r.mux.Lock() - - fd := r.functionData(function) - fd.UpdateDataAny(data, nil, nil) - - r.mux.Unlock() - - r.Device().NotifySubscribers(r.Address(), fd.NotifyCmdType(nil, nil, false, nil)) -} - -func (r *FeatureLocalImpl) AddResultHandler(handler FeatureResult) { - r.resultHandler = append(r.resultHandler, handler) -} - -func (r *FeatureLocalImpl) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg ResultMessage)) { - r.muxResultCB.Lock() - defer r.muxResultCB.Unlock() - - r.resultCallback[msgCounterReference] = function -} - -func (r *FeatureLocalImpl) processResultCallbacks(msgCounterReference model.MsgCounterType, msg ResultMessage) { - r.muxResultCB.Lock() - defer r.muxResultCB.Unlock() - - cb, ok := r.resultCallback[msgCounterReference] - if !ok { - return - } - - go cb(msg) - - delete(r.resultCallback, msgCounterReference) -} - -func (r *FeatureLocalImpl) Information() *model.NodeManagementDetailedDiscoveryFeatureInformationType { - var funs []model.FunctionPropertyType - for fun, operations := range r.operations { - var functionType model.FunctionType = model.FunctionType(fun) - sf := model.FunctionPropertyType{ - Function: &functionType, - PossibleOperations: operations.Information(), - } - - funs = append(funs, sf) - } - - res := model.NodeManagementDetailedDiscoveryFeatureInformationType{ - Description: &model.NetworkManagementFeatureDescriptionDataType{ - FeatureAddress: r.Address(), - FeatureType: &r.ftype, - Role: &r.role, - Description: r.description, - SupportedFunction: funs, - }, - } - - return &res -} - -func (r *FeatureLocalImpl) RequestData( - function model.FunctionType, - selector any, - elements any, - destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) { - fd := r.functionData(function) - cmd := fd.ReadCmdType(selector, elements) - - return r.RequestDataBySenderAddress(cmd, destination.Sender(), destination.Device().Ski(), destination.Address(), destination.MaxResponseDelayDuration()) -} - -func (r *FeatureLocalImpl) RequestDataBySenderAddress( - cmd model.CmdType, - sender Sender, - deviceSki string, - destinationAddress *model.FeatureAddressType, - maxDelay time.Duration) (*model.MsgCounterType, *model.ErrorType) { - - msgCounter, err := sender.Request(model.CmdClassifierTypeRead, r.Address(), destinationAddress, false, []model.CmdType{cmd}) - if err == nil { - r.pendingRequests.Add(deviceSki, *msgCounter, maxDelay) - return msgCounter, nil - } - - return msgCounter, model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) -} - -// Wait and return the response from destination for a message with the msgCounter ID -// this will block until the response is received -func (r *FeatureLocalImpl) FetchRequestData( - msgCounter model.MsgCounterType, - destination FeatureRemote) (any, *model.ErrorType) { - - return r.pendingRequests.GetData(destination.Device().Ski(), msgCounter) -} - -// Subscribe to a remote feature -func (r *FeatureLocalImpl) Subscribe(remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { - if remoteAddress.Device == nil { - return nil, model.NewErrorTypeFromString("device not found") - } - remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) - if remoteDevice == nil { - return nil, model.NewErrorTypeFromString("device not found") - } - - if r.Role() == model.RoleTypeServer { - return nil, model.NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a subscription", r)) - } - - msgCounter, err := remoteDevice.Sender().Subscribe(r.Address(), remoteAddress, r.ftype) - if err != nil { - return nil, model.NewErrorTypeFromString(err.Error()) - } - - r.mux.Lock() - r.subscriptions = append(r.subscriptions, remoteAddress) - r.mux.Unlock() - - return msgCounter, nil -} - -// Remove a subscriptions to a remote feature -func (r *FeatureLocalImpl) RemoveSubscription(remoteAddress *model.FeatureAddressType) { - remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) - if remoteDevice == nil { - return - } - - if _, err := remoteDevice.Sender().Unsubscribe(r.Address(), remoteAddress); err != nil { - return - } - - var subscriptions []*model.FeatureAddressType - - r.mux.Lock() - defer r.mux.Unlock() - - for _, item := range r.subscriptions { - if reflect.DeepEqual(item, remoteAddress) { - continue - } - - subscriptions = append(subscriptions, item) - } - - r.subscriptions = subscriptions -} - -// Remove all subscriptions to remote features -func (r *FeatureLocalImpl) RemoveAllSubscriptions() { - for _, item := range r.subscriptions { - r.RemoveSubscription(item) - } -} - -// Bind to a remote feature -func (r *FeatureLocalImpl) Bind(remoteAddress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType) { - remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) - if remoteDevice == nil { - return nil, model.NewErrorTypeFromString("device not found") - } - - if r.Role() == model.RoleTypeServer { - return nil, model.NewErrorTypeFromString(fmt.Sprintf("the server feature '%s' cannot request a binding", r)) - } - - msgCounter, err := remoteDevice.Sender().Bind(r.Address(), remoteAddress, r.ftype) - if err != nil { - return nil, model.NewErrorTypeFromString(err.Error()) - } - - r.mux.Lock() - r.bindings = append(r.bindings, remoteAddress) - r.mux.Unlock() - - return msgCounter, nil -} - -// Remove a binding to a remote feature -func (r *FeatureLocalImpl) RemoveBinding(remoteAddress *model.FeatureAddressType) { - remoteDevice := r.entity.Device().RemoteDeviceForAddress(*remoteAddress.Device) - if remoteDevice == nil { - return - } - - if _, err := remoteDevice.Sender().Unbind(r.Address(), remoteAddress); err != nil { - return - } - - var bindings []*model.FeatureAddressType - - r.mux.Lock() - defer r.mux.Unlock() - - for _, item := range r.bindings { - if reflect.DeepEqual(item, remoteAddress) { - continue - } - - bindings = append(bindings, item) - } - - r.bindings = bindings -} - -// Remove all subscriptions to remote features -func (r *FeatureLocalImpl) RemoveAllBindings() { - for _, item := range r.bindings { - r.RemoveBinding(item) - } -} - -// Send a notification message with the current data of function to the destination -func (r *FeatureLocalImpl) NotifyData( - function model.FunctionType, - deleteSelector, partialSelector any, - partialWithoutSelector bool, - deleteElements any, - destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) { - fd := r.functionData(function) - cmd := fd.NotifyCmdType(deleteSelector, partialSelector, partialWithoutSelector, deleteElements) - - msgCounter, err := destination.Sender().Request(model.CmdClassifierTypeRead, r.Address(), destination.Address(), false, []model.CmdType{cmd}) - if err != nil { - return nil, model.NewErrorTypeFromString(err.Error()) - } - return msgCounter, nil -} - -// Send a write message with provided data of function to the destination -func (r *FeatureLocalImpl) WriteData( - function model.FunctionType, - deleteSelector, partialSelector any, - deleteElements any, - destination FeatureRemote) (*model.MsgCounterType, *model.ErrorType) { - fd := r.functionData(function) - cmd := fd.WriteCmdType(deleteSelector, partialSelector, deleteElements) - - msgCounter, err := destination.Sender().Write(r.Address(), destination.Address(), cmd) - if err != nil { - return nil, model.NewErrorTypeFromString(err.Error()) - } - - return msgCounter, nil -} - -func (r *FeatureLocalImpl) HandleMessage(message *Message) *model.ErrorType { - if message.Cmd.ResultData != nil { - return r.processResult(message) - } - - cmdData, err := message.Cmd.Data() - if err != nil { - return model.NewErrorType(model.ErrorNumberTypeCommandNotSupported, err.Error()) - } - if cmdData.Function == nil { - return model.NewErrorType(model.ErrorNumberTypeCommandNotSupported, "No function found for cmd data") - } - - switch message.CmdClassifier { - case model.CmdClassifierTypeRead: - if err := r.processRead(*cmdData.Function, message.RequestHeader, message.FeatureRemote); err != nil { - return err - } - case model.CmdClassifierTypeReply: - if err := r.processReply(*cmdData.Function, cmdData.Value, message.FilterPartial, message.FilterDelete, message.RequestHeader, message.FeatureRemote); err != nil { - return err - } - case model.CmdClassifierTypeNotify: - if err := r.processNotify(*cmdData.Function, cmdData.Value, message.FilterPartial, message.FilterDelete, message.FeatureRemote); err != nil { - return err - } - default: - return model.NewErrorTypeFromString(fmt.Sprintf("CmdClassifier not implemented: %s", message.CmdClassifier)) - } - - return nil -} - -func (r *FeatureLocalImpl) processResult(message *Message) *model.ErrorType { - switch message.CmdClassifier { - case model.CmdClassifierTypeResult: - if *message.Cmd.ResultData.ErrorNumber != model.ErrorNumberTypeNoError { - // error numbers explained in Resource Spec 3.11 - errorString := fmt.Sprintf("Error Result received %d", *message.Cmd.ResultData.ErrorNumber) - if message.Cmd.ResultData.Description != nil { - errorString += fmt.Sprintf(": %s", *message.Cmd.ResultData.Description) - } - logging.Log().Debug(errorString) - } - - // we don't need to populate this error as requests don't require a pendingRequest entry - _ = r.pendingRequests.SetResult(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference, model.NewErrorTypeFromResult(message.Cmd.ResultData)) - - if message.RequestHeader.MsgCounterReference == nil { - return nil - } - - // call the Features Error Handler - errorMsg := ResultMessage{ - MsgCounterReference: *message.RequestHeader.MsgCounterReference, - Result: message.Cmd.ResultData, - FeatureLocal: r, - FeatureRemote: message.FeatureRemote, - DeviceRemote: message.DeviceRemote, - } - - if r.resultHandler != nil { - for _, item := range r.resultHandler { - go item.HandleResult(errorMsg) - } - } - - r.processResultCallbacks(*message.RequestHeader.MsgCounterReference, errorMsg) - - return nil - - default: - return model.NewErrorType( - model.ErrorNumberTypeGeneralError, - fmt.Sprintf("ResultData CmdClassifierType %s not implemented", message.CmdClassifier)) - } -} - -func (r *FeatureLocalImpl) processRead(function model.FunctionType, requestHeader *model.HeaderType, featureRemote FeatureRemote) *model.ErrorType { - // is this a read request to a local server/special feature? - if r.role == model.RoleTypeClient { - // Read requests to a client feature are not allowed - return model.NewErrorTypeFromNumber(model.ErrorNumberTypeCommandRejected) - } - - cmd := r.functionData(function).ReplyCmdType(false) - if err := featureRemote.Sender().Reply(requestHeader, r.Address(), cmd); err != nil { - return model.NewErrorTypeFromString(err.Error()) - } - - return nil -} - -func (r *FeatureLocalImpl) processReply(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, requestHeader *model.HeaderType, featureRemote FeatureRemote) *model.ErrorType { - featureRemote.UpdateData(function, data, filterPartial, filterDelete) - _ = r.pendingRequests.SetData(featureRemote.Device().Ski(), *requestHeader.MsgCounterReference, data) - // an error in SetData only means that there is no pendingRequest waiting for this dataset - // so this is nothing to consider as an error to return - - // the data was updated, so send an event, other event handlers may watch out for this as well - payload := EventPayload{ - Ski: featureRemote.Device().Ski(), - EventType: EventTypeDataChange, - ChangeType: ElementChangeUpdate, - Feature: featureRemote, - Device: featureRemote.Device(), - Entity: featureRemote.Entity(), - CmdClassifier: util.Ptr(model.CmdClassifierTypeReply), - Data: data, - } - Events.Publish(payload) - - return nil -} - -func (r *FeatureLocalImpl) processNotify(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType, featureRemote FeatureRemote) *model.ErrorType { - featureRemote.UpdateData(function, data, filterPartial, filterDelete) - - payload := EventPayload{ - Ski: featureRemote.Device().Ski(), - EventType: EventTypeDataChange, - ChangeType: ElementChangeUpdate, - Feature: featureRemote, - Device: featureRemote.Device(), - Entity: featureRemote.Entity(), - CmdClassifier: util.Ptr(model.CmdClassifierTypeNotify), - Data: data, - } - Events.Publish(payload) - - return nil -} - -func (r *FeatureLocalImpl) functionData(function model.FunctionType) FunctionDataCmd { - fd, found := r.functionDataMap[function] - if !found { - panic(fmt.Errorf("Data was not found for function '%s'", function)) - } - return fd -} diff --git a/spine/feature_local_test.go b/spine/feature_local_test.go deleted file mode 100644 index bb113633..00000000 --- a/spine/feature_local_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package spine - -import ( - "testing" - - "github.com/enbility/eebus-go/spine/mocks" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" -) - -func TestDeviceClassificationSuite(t *testing.T) { - suite.Run(t, new(DeviceClassificationTestSuite)) -} - -type DeviceClassificationTestSuite struct { - suite.Suite - senderMock *mocks.Sender - function model.FunctionType - featureType, subFeatureType model.FeatureTypeType - msgCounter model.MsgCounterType - remoteFeature, remoteSubFeature FeatureRemote - localFeature FeatureLocal - localServerFeature FeatureLocal -} - -func (suite *DeviceClassificationTestSuite) BeforeTest(suiteName, testName string) { - suite.senderMock = mocks.NewSender(suite.T()) - suite.function = model.FunctionTypeDeviceClassificationManufacturerData - suite.featureType = model.FeatureTypeTypeDeviceClassification - suite.subFeatureType = model.FeatureTypeTypeMeasurement - suite.msgCounter = model.MsgCounterType(1) - - _, suite.remoteFeature = createRemoteDeviceAndFeature(1, suite.featureType, suite.senderMock) - _, suite.remoteSubFeature = createRemoteDeviceAndFeature(2, suite.subFeatureType, suite.senderMock) - suite.localFeature, suite.localServerFeature = createLocalDeviceAndFeature(1, suite.featureType) -} - -func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Reply() { - suite.senderMock.On("Request", model.CmdClassifierTypeRead, suite.localFeature.Address(), suite.remoteFeature.Address(), false, mock.AnythingOfType("[]model.CmdType")).Return(&suite.msgCounter, nil) - - // send data request - msgCounter, err := suite.localFeature.RequestData(suite.function, nil, nil, suite.remoteFeature) - assert.Nil(suite.T(), err) - - manufacturerData := &model.DeviceClassificationManufacturerDataType{ - BrandName: util.Ptr(model.DeviceClassificationStringType("brand name")), - VendorName: util.Ptr(model.DeviceClassificationStringType("vendor name")), - DeviceName: util.Ptr(model.DeviceClassificationStringType("device name")), - DeviceCode: util.Ptr(model.DeviceClassificationStringType("device code")), - SerialNumber: util.Ptr(model.DeviceClassificationStringType("serial number")), - } - - replyMsg := Message{ - Cmd: model.CmdType{ - DeviceClassificationManufacturerData: manufacturerData, - }, - CmdClassifier: model.CmdClassifierTypeReply, - RequestHeader: &model.HeaderType{ - MsgCounter: util.Ptr(model.MsgCounterType(1)), - MsgCounterReference: &suite.msgCounter, - }, - FeatureRemote: suite.remoteFeature, - } - // set response - msgErr := suite.localFeature.HandleMessage(&replyMsg) - if assert.Nil(suite.T(), msgErr) { - remoteData := suite.remoteFeature.DataCopy(suite.function) - assert.IsType(suite.T(), &model.DeviceClassificationManufacturerDataType{}, remoteData, "Data has wrong type") - } - - // Act - result, err := suite.localFeature.FetchRequestData(*msgCounter, suite.remoteFeature) - assert.Nil(suite.T(), err) - assert.NotNil(suite.T(), result) - assert.IsType(suite.T(), &model.DeviceClassificationManufacturerDataType{}, result, "Data has wrong type") - receivedData := result.(*model.DeviceClassificationManufacturerDataType) - - assert.Equal(suite.T(), manufacturerData.BrandName, receivedData.BrandName) - assert.Equal(suite.T(), manufacturerData.VendorName, receivedData.VendorName) - assert.Equal(suite.T(), manufacturerData.DeviceName, receivedData.DeviceName) - assert.Equal(suite.T(), manufacturerData.DeviceCode, receivedData.DeviceCode) - assert.Equal(suite.T(), manufacturerData.SerialNumber, receivedData.SerialNumber) -} - -func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Request_Error() { - suite.senderMock.On("Request", model.CmdClassifierTypeRead, suite.localFeature.Address(), suite.remoteFeature.Address(), false, mock.AnythingOfType("[]model.CmdType")).Return(&suite.msgCounter, nil) - - const errorNumber = model.ErrorNumberTypeGeneralError - const errorDescription = "error occurred" - - // send data request - msgCounter, err := suite.localFeature.RequestData(suite.function, nil, nil, suite.remoteFeature) - assert.Nil(suite.T(), err) - - replyMsg := Message{ - Cmd: model.CmdType{ - ResultData: &model.ResultDataType{ - ErrorNumber: util.Ptr(model.ErrorNumberType(errorNumber)), - Description: util.Ptr(model.DescriptionType(errorDescription)), - }, - }, - CmdClassifier: model.CmdClassifierTypeResult, - RequestHeader: &model.HeaderType{ - MsgCounter: util.Ptr(model.MsgCounterType(1)), - MsgCounterReference: &suite.msgCounter, - }, - FeatureRemote: suite.remoteFeature, - EntityRemote: suite.remoteFeature.Entity(), - DeviceRemote: suite.remoteFeature.Device(), - } - - // set response - msgErr := suite.localFeature.HandleMessage(&replyMsg) - if assert.Nil(suite.T(), msgErr) { - remoteData := suite.remoteFeature.DataCopy(suite.function) - assert.Nil(suite.T(), remoteData) - } - - // Act - result, err := suite.localFeature.FetchRequestData(*msgCounter, suite.remoteFeature) - assert.Nil(suite.T(), result) - assert.NotNil(suite.T(), err) - assert.Equal(suite.T(), errorNumber, err.ErrorNumber) - assert.Equal(suite.T(), errorDescription, string(*err.Description)) -} - -func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Subscribiptions() { - suite.senderMock.On("Subscribe", mock.Anything, mock.Anything, mock.Anything).Return(&suite.msgCounter, nil) - suite.senderMock.On("Unsubscribe", mock.Anything, mock.Anything, mock.Anything).Return(&suite.msgCounter, nil) - - msgCounter, err := suite.localFeature.Subscribe(suite.remoteFeature.Address()) - assert.NotNil(suite.T(), err) - assert.Nil(suite.T(), msgCounter) - - suite.localFeature.RemoveSubscription(suite.remoteFeature.Address()) - - suite.localFeature.Device().AddRemoteDeviceForSki(suite.remoteFeature.Device().Ski(), suite.remoteFeature.Device()) - - msgCounter, err = suite.localServerFeature.Subscribe(suite.remoteFeature.Address()) - assert.NotNil(suite.T(), err) - assert.Nil(suite.T(), msgCounter) - - suite.localFeature.RemoveSubscription(suite.remoteFeature.Address()) - - msgCounter, err = suite.localFeature.Subscribe(suite.remoteFeature.Address()) - assert.Nil(suite.T(), err) - assert.NotNil(suite.T(), msgCounter) - - msgCounter, err = suite.localFeature.Subscribe(suite.remoteSubFeature.Address()) - assert.Nil(suite.T(), err) - assert.NotNil(suite.T(), msgCounter) - - suite.localFeature.RemoveSubscription(suite.remoteFeature.Address()) - - suite.localFeature.RemoveAllSubscriptions() -} - -func (suite *DeviceClassificationTestSuite) TestDeviceClassification_Bindings() { - suite.senderMock.On("Bind", mock.Anything, mock.Anything, mock.Anything).Return(&suite.msgCounter, nil) - suite.senderMock.On("Unbind", mock.Anything, mock.Anything, mock.Anything).Return(&suite.msgCounter, nil) - - msgCounter, err := suite.localFeature.Bind(suite.remoteFeature.Address()) - assert.NotNil(suite.T(), err) - assert.Nil(suite.T(), msgCounter) - - suite.localFeature.RemoveBinding(suite.remoteFeature.Address()) - - suite.localFeature.Device().AddRemoteDeviceForSki(suite.remoteFeature.Device().Ski(), suite.remoteFeature.Device()) - - msgCounter, err = suite.localServerFeature.Bind(suite.remoteFeature.Address()) - assert.NotNil(suite.T(), err) - assert.Nil(suite.T(), msgCounter) - - suite.localFeature.RemoveBinding(suite.remoteFeature.Address()) - - msgCounter, err = suite.localFeature.Bind(suite.remoteFeature.Address()) - assert.Nil(suite.T(), err) - assert.NotNil(suite.T(), msgCounter) - - msgCounter, err = suite.localFeature.Bind(suite.remoteSubFeature.Address()) - assert.Nil(suite.T(), err) - assert.NotNil(suite.T(), msgCounter) - - suite.localFeature.RemoveBinding(suite.remoteFeature.Address()) - - suite.localFeature.RemoveAllBindings() -} diff --git a/spine/feature_remote.go b/spine/feature_remote.go deleted file mode 100644 index adef9945..00000000 --- a/spine/feature_remote.go +++ /dev/null @@ -1,114 +0,0 @@ -package spine - -import ( - "fmt" - "sync" - "time" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/rickb777/date/period" -) - -const defaultMaxResponseDelay = time.Duration(time.Second * 10) - -var _ FeatureRemote = (*FeatureRemoteImpl)(nil) - -type FeatureRemoteImpl struct { - *FeatureImpl - - entity EntityRemote - functionDataMap map[model.FunctionType]FunctionData - maxResponseDelay *time.Duration - - mux sync.Mutex -} - -func NewFeatureRemoteImpl(id uint, entity EntityRemote, ftype model.FeatureTypeType, role model.RoleType) *FeatureRemoteImpl { - res := &FeatureRemoteImpl{ - FeatureImpl: NewFeatureImpl( - featureAddressType(id, entity.Address()), - ftype, - role), - entity: entity, - functionDataMap: make(map[model.FunctionType]FunctionData), - } - for _, fd := range CreateFunctionData[FunctionData](ftype) { - res.functionDataMap[fd.Function()] = fd - } - - res.operations = make(map[model.FunctionType]*Operations) - - return res -} - -func (r *FeatureRemoteImpl) DataCopy(function model.FunctionType) any { - r.mux.Lock() - defer r.mux.Unlock() - - return r.functionData(function).DataCopyAny() -} - -func (r *FeatureRemoteImpl) SetData(function model.FunctionType, data any) { - r.mux.Lock() - - fd := r.functionData(function) - fd.UpdateDataAny(data, nil, nil) - - r.mux.Unlock() -} - -func (r *FeatureRemoteImpl) UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType) { - r.mux.Lock() - defer r.mux.Unlock() - - r.functionData(function).UpdateDataAny(data, filterPartial, filterDelete) - // TODO: fire event -} - -func (r *FeatureRemoteImpl) Sender() Sender { - return r.Device().Sender() -} - -func (r *FeatureRemoteImpl) Device() DeviceRemote { - return r.entity.Device() -} - -func (r *FeatureRemoteImpl) Entity() EntityRemote { - return r.entity -} - -func (r *FeatureRemoteImpl) SetOperations(functions []model.FunctionPropertyType) { - r.operations = make(map[model.FunctionType]*Operations) - for _, sf := range functions { - r.operations[*sf.Function] = NewOperations(sf.PossibleOperations.Read != nil, sf.PossibleOperations.Write != nil) - } -} - -func (r *FeatureRemoteImpl) SetMaxResponseDelay(delay *model.MaxResponseDelayType) { - if delay == nil { - return - } - p, err := period.Parse(string(*delay)) - if err != nil { - r.maxResponseDelay = util.Ptr(p.DurationApprox()) - } else { - logging.Log().Debug(err) - } -} - -func (r *FeatureRemoteImpl) MaxResponseDelayDuration() time.Duration { - if r.maxResponseDelay != nil { - return *r.maxResponseDelay - } - return defaultMaxResponseDelay -} - -func (r *FeatureRemoteImpl) functionData(function model.FunctionType) FunctionData { - fd, found := r.functionDataMap[function] - if !found { - panic(fmt.Errorf("Data was not found for function '%s'", function)) - } - return fd -} diff --git a/spine/function_data.go b/spine/function_data.go deleted file mode 100644 index 1e120920..00000000 --- a/spine/function_data.go +++ /dev/null @@ -1,82 +0,0 @@ -package spine - -import ( - "fmt" - "sync" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -var _ FunctionData = (*FunctionDataImpl[int])(nil) - -type FunctionDataImpl[T any] struct { - functionType model.FunctionType - data *T - - mux sync.Mutex -} - -func NewFunctionData[T any](function model.FunctionType) *FunctionDataImpl[T] { - return &FunctionDataImpl[T]{ - functionType: function, - } -} - -func (r *FunctionDataImpl[T]) Function() model.FunctionType { - return r.functionType -} - -func (r *FunctionDataImpl[T]) DataCopy() *T { - r.mux.Lock() - defer r.mux.Unlock() - - // copy the data and return it as the data can be updated - // and newly assigned at any time otherwise we run into panics - // because of invalid memory address or nil pointer dereference - var copiedData T - if r.data == nil { - return nil - } - - copiedData = *r.data - - return &copiedData -} - -func (r *FunctionDataImpl[T]) UpdateData(newData *T, filterPartial *model.FilterType, filterDelete *model.FilterType) *model.ErrorType { - r.mux.Lock() - defer r.mux.Unlock() - - if filterPartial == nil && filterDelete == nil { - // just set the data - r.data = newData - return nil - } - - supported := util.Implements[T, model.Updater]() - if !supported { - return model.NewErrorTypeFromString(fmt.Sprintf("partial updates are not supported for type '%s'", util.Type[T]().Name())) - } - - if r.data == nil { - r.data = new(T) - } - - updater := any(r.data).(model.Updater) - updater.UpdateList(newData, filterPartial, filterDelete) - - return nil -} - -func (r *FunctionDataImpl[T]) DataCopyAny() any { - return r.DataCopy() -} - -func (r *FunctionDataImpl[T]) UpdateDataAny(newData any, filterPartial *model.FilterType, filterDelete *model.FilterType) { - err := r.UpdateData(newData.(*T), filterPartial, filterDelete) - if err != nil { - logging.Log().Debug(err.String()) - } -} diff --git a/spine/function_data_cmd.go b/spine/function_data_cmd.go deleted file mode 100644 index ef3d2f8b..00000000 --- a/spine/function_data_cmd.go +++ /dev/null @@ -1,120 +0,0 @@ -package spine - -import ( - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -var _ FunctionDataCmd = (*FunctionDataCmdImpl[int])(nil) - -type FunctionDataCmdImpl[T any] struct { - *FunctionDataImpl[T] -} - -func NewFunctionDataCmd[T any](function model.FunctionType) *FunctionDataCmdImpl[T] { - return &FunctionDataCmdImpl[T]{ - FunctionDataImpl: NewFunctionData[T](function), - } -} - -func (r *FunctionDataCmdImpl[T]) ReadCmdType(partialSelector any, elements any) model.CmdType { - cmd := createCmd[T](r.functionType, nil) - - var filters []model.FilterType - filters = filtersForSelectorsElements(r.functionType, filters, nil, partialSelector, nil, elements) - if len(filters) > 0 { - cmd.Filter = filters - } - - return cmd -} - -func (r *FunctionDataCmdImpl[T]) ReplyCmdType(partial bool) model.CmdType { - cmd := createCmd(r.functionType, r.data) - if partial { - cmd.Filter = filterEmptyPartial() - } - return cmd -} - -func (r *FunctionDataCmdImpl[T]) NotifyCmdType(deleteSelector, partialSelector any, partialWithoutSelector bool, deleteElements any) model.CmdType { - cmd := createCmd(r.functionType, r.data) - cmd.Function = util.Ptr(model.FunctionType(r.functionType)) - - if partialWithoutSelector { - cmd.Filter = filterEmptyPartial() - return cmd - } - var filters []model.FilterType - if filters := filtersForSelectorsElements(r.functionType, filters, deleteSelector, partialSelector, deleteElements, nil); len(filters) > 0 { - cmd.Filter = filters - } - - return cmd -} - -func (r *FunctionDataCmdImpl[T]) WriteCmdType(deleteSelector, partialSelector any, deleteElements any) model.CmdType { - cmd := createCmd(r.functionType, r.data) - - var filters []model.FilterType - if filters := filtersForSelectorsElements(r.functionType, filters, deleteSelector, partialSelector, deleteElements, nil); len(filters) > 0 { - cmd.Filter = filters - } - - return cmd -} - -func filtersForSelectorsElements(functionType model.FunctionType, filters []model.FilterType, deleteSelector, partialSelector any, deleteElements, readElements any) []model.FilterType { - if deleteSelector != nil || deleteElements != nil { - filter := model.FilterType{CmdControl: &model.CmdControlType{Delete: &model.ElementTagType{}}} - if deleteSelector != nil { - filter = addSelectorToFilter(filter, functionType, &deleteSelector) - } - if deleteElements != nil { - filter = addElementToFilter(filter, functionType, &deleteElements) - } - filters = append(filters, filter) - } - - if partialSelector != nil || readElements != nil { - filter := model.FilterType{CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}} - if partialSelector != nil { - filter = addSelectorToFilter(filter, functionType, &partialSelector) - } - if readElements != nil { - filter = addElementToFilter(filter, functionType, &readElements) - } - filters = append(filters, filter) - } - - return filters -} - -// simple helper for adding a single filterType without any selectors -func filterEmptyPartial() []model.FilterType { - return []model.FilterType{{CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}}} -} - -func addSelectorToFilter[T any](filter model.FilterType, function model.FunctionType, data *T) model.FilterType { - result := filter - - result.SetDataForFunction(model.EEBusTagTypeTypeSelector, function, data) - - return result -} - -func addElementToFilter[T any](filter model.FilterType, function model.FunctionType, data *T) model.FilterType { - result := filter - - result.SetDataForFunction(model.EEbusTagTypeTypeElements, function, data) - - return result -} - -func createCmd[T any](function model.FunctionType, data *T) model.CmdType { - result := model.CmdType{} - - result.SetDataForFunction(function, data) - - return result -} diff --git a/spine/function_data_cmd_test.go b/spine/function_data_cmd_test.go deleted file mode 100644 index 34ccf883..00000000 --- a/spine/function_data_cmd_test.go +++ /dev/null @@ -1,1140 +0,0 @@ -package spine - -import ( - "testing" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestFunctionDataCmdSuite(t *testing.T) { - suite.Run(t, new(FctDataCmdSuite)) -} - -type FctDataCmdSuite struct { - suite.Suite - function model.FunctionType - data *model.DeviceClassificationManufacturerDataType - sut *FunctionDataCmdImpl[model.DeviceClassificationManufacturerDataType] -} - -func (suite *FctDataCmdSuite) SetupSuite() { - suite.function = model.FunctionTypeDeviceClassificationManufacturerData - suite.data = &model.DeviceClassificationManufacturerDataType{ - DeviceName: util.Ptr(model.DeviceClassificationStringType("device name")), - } - suite.sut = NewFunctionDataCmd[model.DeviceClassificationManufacturerDataType](suite.function) - suite.sut.UpdateData(suite.data, nil, nil) -} - -func (suite *FctDataCmdSuite) TestFunctionDataCmd_ReadCmd() { - readCmd := suite.sut.ReadCmdType(nil, nil) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Nil(suite.T(), readCmd.DeviceClassificationManufacturerData.DeviceName) - - partialS := model.NewFilterTypePartial() - readCmd = suite.sut.ReadCmdType(partialS, nil) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Nil(suite.T(), readCmd.DeviceClassificationManufacturerData.DeviceName) -} - -func (suite *FctDataCmdSuite) TestFunctionDataCmd_ReplyCmd() { - readCmd := suite.sut.ReplyCmdType(false) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Equal(suite.T(), suite.data.DeviceName, readCmd.DeviceClassificationManufacturerData.DeviceName) - - readCmd = suite.sut.ReplyCmdType(true) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Equal(suite.T(), suite.data.DeviceName, readCmd.DeviceClassificationManufacturerData.DeviceName) -} - -func (suite *FctDataCmdSuite) TestFunctionDataCmd_NotifyCmd() { - readCmd := suite.sut.NotifyCmdType(nil, nil, false, nil) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Equal(suite.T(), suite.data.DeviceName, readCmd.DeviceClassificationManufacturerData.DeviceName) - - readCmd = suite.sut.NotifyCmdType(nil, nil, true, nil) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Equal(suite.T(), suite.data.DeviceName, readCmd.DeviceClassificationManufacturerData.DeviceName) - - deleteS := model.NewFilterTypePartial() - readCmd = suite.sut.NotifyCmdType(deleteS, nil, false, nil) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Equal(suite.T(), suite.data.DeviceName, readCmd.DeviceClassificationManufacturerData.DeviceName) -} - -func (suite *FctDataCmdSuite) TestFunctionDataCmd_WriteCmd() { - readCmd := suite.sut.WriteCmdType(nil, nil, nil) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Equal(suite.T(), suite.data.DeviceName, readCmd.DeviceClassificationManufacturerData.DeviceName) - - partialS := model.NewFilterTypePartial() - readCmd = suite.sut.WriteCmdType(nil, partialS, nil) - assert.NotNil(suite.T(), readCmd.DeviceClassificationManufacturerData) - assert.Equal(suite.T(), suite.data.DeviceName, readCmd.DeviceClassificationManufacturerData.DeviceName) -} - -func (suite *FctDataCmdSuite) Test_AddSelectorToFilter() { - filter := model.FilterType{CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}} - - result := addSelectorToFilter(filter, model.FunctionTypeAlarmListData, &model.AlarmListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeBillConstraintsListData, &model.BillConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeBillDescriptionListData, &model.BillDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeBillListData, &model.BillListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeBindingManagementEntryListData, &model.BindingManagementEntryListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeCommodityListData, &model.CommodityListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeDeviceConfigurationKeyValueConstraintsListData, &model.DeviceConfigurationKeyValueConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, &model.DeviceConfigurationKeyValueDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeDeviceConfigurationKeyValueListData, &model.DeviceConfigurationKeyValueListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeDirectControlActivityListData, &model.DirectControlActivityListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeElectricalConnectionParameterDescriptionListData, &model.ElectricalConnectionParameterDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeElectricalConnectionPermittedValueSetListData, &model.ElectricalConnectionPermittedValueSetListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeElectricalConnectionStateListData, &model.ElectricalConnectionStateListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeHvacOperationModeDescriptionListData, &model.HvacOperationModeDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeHvacOverrunDescriptionListData, &model.HvacOverrunDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeHvacOverrunListData, &model.HvacOverrunListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeHvacSystemFunctionDescriptionListData, &model.HvacSystemFunctionDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeHvacSystemFunctionListData, &model.HvacSystemFunctionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeHvacSystemFunctionOperationModeRelationListData, &model.HvacSystemFunctionOperationModeRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeHvacSystemFunctionPowerSequenceRelationListData, &model.HvacSystemFunctionPowerSequenceRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeHvacSystemFunctionSetPointRelationListData, &model.HvacSystemFunctionSetpointRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeIdentificationListData, &model.IdentificationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeIncentiveDescriptionListData, &model.IncentiveDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeIncentiveListData, &model.IncentiveListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeIncentiveTableConstraintsData, &model.IncentiveTableConstraintsDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeIncentiveTableData, &model.IncentiveTableDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeIncentiveTableDescriptionData, &model.IncentiveTableDescriptionDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeLoadControlEventListData, &model.LoadControlEventListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeLoadControlLimitConstraintsListData, &model.LoadControlLimitConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeLoadControlLimitDescriptionListData, &model.LoadControlLimitDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeLoadControlLimitListData, &model.LoadControlLimitListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeLoadControlStateListData, &model.LoadControlStateListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeMeasurementConstraintsListData, &model.MeasurementConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeMeasurementDescriptionListData, &model.MeasurementDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeMeasurementListData, &model.MeasurementListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeMeasurementThresholdRelationListData, &model.MeasurementThresholdRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeMessagingListData, &model.MessagingListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeNetworkManagementDeviceDescriptionListData, &model.NetworkManagementDeviceDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeNetworkManagementEntityDescriptionListData, &model.NetworkManagementEntityDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeNetworkManagementFeatureDescriptionListData, &model.NetworkManagementFeatureDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeNodeManagementBindingData, &model.NodeManagementBindingDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeNodeManagementDestinationListData, &model.NodeManagementDestinationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeNodeManagementDetailedDiscoveryData, &model.NodeManagementDetailedDiscoveryDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeNodeManagementSubscriptionData, &model.NodeManagementSubscriptionDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeNodeManagementUseCaseData, &model.NodeManagementUseCaseDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeOperatingConstraintsDurationListData, &model.OperatingConstraintsDurationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeOperatingConstraintsInterruptListData, &model.OperatingConstraintsInterruptListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeOperatingConstraintsPowerDescriptionListData, &model.OperatingConstraintsPowerDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeOperatingConstraintsPowerLevelListData, &model.OperatingConstraintsPowerLevelListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeOperatingConstraintsPowerRangeListData, &model.OperatingConstraintsPowerRangeListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeOperatingConstraintsResumeImplicationListData, &model.OperatingConstraintsResumeImplicationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerSequenceAlternativesRelationListData, &model.PowerSequenceAlternativesRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerSequenceDescriptionListData, &model.PowerSequenceDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerSequencePriceListData, &model.PowerSequencePriceListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerSequenceScheduleConstraintsListData, &model.PowerSequenceScheduleConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerSequenceScheduleListData, &model.PowerSequenceScheduleListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerSequenceSchedulePreferenceListData, &model.PowerSequenceSchedulePreferenceListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerSequenceStateListData, &model.PowerSequenceStateListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerTimeSlotScheduleConstraintsListData, &model.PowerTimeSlotScheduleConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerTimeSlotScheduleListData, &model.PowerTimeSlotScheduleListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypePowerTimeSlotValueListData, &model.PowerTimeSlotValueListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSensingListData, &model.SensingListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSetpointConstraintsListData, &model.SetpointConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSetpointDescriptionListData, &model.SetpointDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSetpointListData, &model.SetpointListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSmartEnergyManagementPsData, &model.SmartEnergyManagementPsDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSmartEnergyManagementPsPriceData, &model.SmartEnergyManagementPsPriceDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSpecificationVersionListData, &model.SpecificationVersionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSupplyConditionListData, &model.SupplyConditionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeSupplyConditionThresholdRelationListData, &model.SupplyConditionThresholdRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTariffBoundaryRelationListData, &model.TariffBoundaryRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTariffDescriptionListData, &model.TariffDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTariffListData, &model.TariffListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTariffTierRelationListData, &model.TariffTierRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTaskManagementJobDescriptionListData, &model.TaskManagementJobDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTaskManagementJobListData, &model.TaskManagementJobListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTaskManagementJobRelationListData, &model.TaskManagementJobRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeThresholdConstraintsListData, &model.ThresholdConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeThresholdDescriptionListData, &model.ThresholdDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeThresholdListData, &model.ThresholdListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTierBoundaryDescriptionListData, &model.TierBoundaryDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTierBoundaryListData, &model.TierBoundaryListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTierDescriptionListData, &model.TierDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTierIncentiveRelationListData, &model.TierIncentiveRelationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTierListData, &model.TierListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTimeSeriesConstraintsListData, &model.TimeSeriesConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTimeSeriesDescriptionListData, &model.TimeSeriesDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTimeSeriesListData, &model.TimeSeriesListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTimeTableConstraintsListData, &model.TimeTableConstraintsListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTimeTableDescriptionListData, &model.TimeTableDescriptionListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeTimeTableListData, &model.TimeTableListDataSelectorsType{}) - assert.NotNil(suite.T(), result) - - result = addSelectorToFilter(filter, model.FunctionTypeUseCaseInformationListData, &model.UseCaseInformationListDataSelectorsType{}) - assert.NotNil(suite.T(), result) -} - -func (suite *FctDataCmdSuite) Test_AddElementsToFilter() { - filter := model.FilterType{CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}} - - result := addElementToFilter(filter, model.FunctionTypeActuatorLevelData, &model.ActuatorLevelDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeActuatorLevelDescriptionData, &model.ActuatorLevelDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeActuatorSwitchData, &model.ActuatorSwitchDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeActuatorSwitchDescriptionData, &model.ActuatorSwitchDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeAlarmListData, &model.AlarmDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeBillConstraintsListData, &model.BillConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeBillDescriptionListData, &model.BillDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeBillListData, &model.BillDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeBindingManagementDeleteCall, &model.BindingManagementDeleteCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeBindingManagementEntryListData, &model.BindingManagementEntryDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeBindingManagementRequestCall, &model.BindingManagementRequestCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeCommodityListData, &model.CommodityDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDataTunnelingCall, &model.DataTunnelingCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDeviceClassificationManufacturerData, &model.DeviceClassificationManufacturerDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDeviceClassificationUserData, &model.DeviceClassificationUserDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDeviceConfigurationKeyValueConstraintsListData, &model.DeviceConfigurationKeyValueConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, &model.DeviceConfigurationKeyValueDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDeviceConfigurationKeyValueListData, &model.DeviceConfigurationKeyValueDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDeviceDiagnosisHeartbeatData, &model.DeviceDiagnosisHeartbeatDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDeviceDiagnosisServiceData, &model.DeviceDiagnosisServiceDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDeviceDiagnosisStateData, &model.DeviceDiagnosisStateDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDirectControlActivityListData, &model.DirectControlActivityDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeDirectControlDescriptionData, &model.DirectControlDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeElectricalConnectionParameterDescriptionListData, &model.ElectricalConnectionParameterDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeElectricalConnectionPermittedValueSetListData, &model.ElectricalConnectionPermittedValueSetDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeElectricalConnectionStateListData, &model.ElectricalConnectionStateDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeHvacOperationModeDescriptionListData, &model.HvacOperationModeDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeHvacOverrunDescriptionListData, &model.HvacOverrunDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeHvacOverrunListData, &model.HvacOverrunDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeHvacSystemFunctionDescriptionListData, &model.HvacSystemFunctionDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeHvacSystemFunctionListData, &model.HvacSystemFunctionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeHvacSystemFunctionOperationModeRelationListData, &model.HvacSystemFunctionOperationModeRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeHvacSystemFunctionPowerSequenceRelationListData, &model.HvacSystemFunctionPowerSequenceRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeHvacSystemFunctionSetPointRelationListData, &model.HvacSystemFunctionSetpointRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeIdentificationListData, &model.IdentificationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeIncentiveDescriptionListData, &model.IncentiveDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeIncentiveListData, &model.IncentiveDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeIncentiveTableConstraintsData, &model.IncentiveTableConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeIncentiveTableData, &model.IncentiveTableDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeIncentiveTableDescriptionData, &model.IncentiveTableDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeLoadControlEventListData, &model.LoadControlEventDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeLoadControlLimitConstraintsListData, &model.LoadControlLimitConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeLoadControlLimitDescriptionListData, &model.LoadControlLimitDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeLoadControlLimitListData, &model.LoadControlLimitDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeLoadControlNodeData, &model.LoadControlNodeDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeLoadControlStateListData, &model.LoadControlStateDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeMeasurementConstraintsListData, &model.MeasurementConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeMeasurementDescriptionListData, &model.MeasurementDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeMeasurementListData, &model.MeasurementDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeMeasurementThresholdRelationListData, &model.MeasurementThresholdRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeMessagingListData, &model.MessagingDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementAbortCall, &model.NetworkManagementAbortCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementAddNodeCall, &model.NetworkManagementAddNodeCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementDeviceDescriptionListData, &model.NetworkManagementDeviceDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementDiscoverCall, &model.NetworkManagementDiscoverCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementEntityDescriptionListData, &model.NetworkManagementEntityDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementFeatureDescriptionListData, &model.NetworkManagementFeatureDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementJoiningModeData, &model.NetworkManagementJoiningModeDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementModifyNodeCall, &model.NetworkManagementModifyNodeCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementProcessStateData, &model.NetworkManagementProcessStateDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementRemoveNodeCall, &model.NetworkManagementRemoveNodeCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementReportCandidateData, &model.NetworkManagementReportCandidateDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNetworkManagementScanNetworkCall, &model.NetworkManagementScanNetworkCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementBindingData, &model.NodeManagementBindingDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementBindingDeleteCall, &model.NodeManagementBindingDeleteCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementBindingRequestCall, &model.NodeManagementBindingRequestCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementDestinationListData, &model.NodeManagementDestinationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementDetailedDiscoveryData, &model.NodeManagementDetailedDiscoveryDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementSubscriptionData, &model.NodeManagementSubscriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementSubscriptionDeleteCall, &model.NodeManagementSubscriptionDeleteCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementSubscriptionRequestCall, &model.NodeManagementSubscriptionRequestCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeNodeManagementUseCaseData, &model.NodeManagementUseCaseDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeOperatingConstraintsDurationListData, &model.OperatingConstraintsDurationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeOperatingConstraintsInterruptListData, &model.OperatingConstraintsInterruptDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeOperatingConstraintsPowerDescriptionListData, &model.OperatingConstraintsPowerDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeOperatingConstraintsPowerLevelListData, &model.OperatingConstraintsPowerLevelDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeOperatingConstraintsPowerRangeListData, &model.OperatingConstraintsPowerRangeDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeOperatingConstraintsResumeImplicationListData, &model.OperatingConstraintsResumeImplicationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequenceAlternativesRelationListData, &model.PowerSequenceAlternativesRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequenceDescriptionListData, &model.PowerSequenceDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequenceNodeScheduleInformationData, &model.PowerSequenceNodeScheduleInformationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequencePriceCalculationRequestCall, &model.PowerSequencePriceCalculationRequestCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequencePriceListData, &model.PowerSequencePriceDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequenceScheduleConfigurationRequestCall, &model.PowerSequenceScheduleConfigurationRequestCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequenceScheduleConstraintsListData, &model.PowerSequenceScheduleConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequenceScheduleListData, &model.PowerSequenceScheduleDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequenceSchedulePreferenceListData, &model.PowerSequenceSchedulePreferenceDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerSequenceStateListData, &model.PowerSequenceStateDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerTimeSlotScheduleConstraintsListData, &model.PowerTimeSlotScheduleConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerTimeSlotScheduleListData, &model.PowerTimeSlotScheduleDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypePowerTimeSlotValueListData, &model.PowerTimeSlotValueDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSensingListData, &model.SensingDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSetpointConstraintsListData, &model.SetpointConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSetpointDescriptionListData, &model.SensingDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSetpointListData, &model.SetpointDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSmartEnergyManagementPsConfigurationRequestCall, &model.SmartEnergyManagementPsConfigurationRequestCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSmartEnergyManagementPsData, &model.SmartEnergyManagementPsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSmartEnergyManagementPsPriceCalculationRequestCall, &model.SmartEnergyManagementPsPriceCalculationRequestCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSmartEnergyManagementPsPriceData, &model.SmartEnergyManagementPsPriceDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSpecificationVersionListData, &model.SpecificationVersionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSubscriptionManagementDeleteCall, &model.SubscriptionManagementDeleteCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSubscriptionManagementEntryListData, &model.SubscriptionManagementEntryDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSubscriptionManagementRequestCall, &model.SubscriptionManagementRequestCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSupplyConditionListData, &model.SupplyConditionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSupplyConditionDescriptionListData, &model.SupplyConditionDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeSupplyConditionThresholdRelationListData, &model.SupplyConditionThresholdRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTariffBoundaryRelationListData, &model.TariffBoundaryRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTariffDescriptionListData, &model.TariffDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTariffListData, &model.TariffDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTariffOverallConstraintsData, &model.TariffOverallConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTariffTierRelationListData, &model.TariffTierRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTaskManagementJobDescriptionListData, &model.TaskManagementJobDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTaskManagementJobListData, &model.TaskManagementJobDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTaskManagementJobRelationListData, &model.TaskManagementJobRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTaskManagementOverviewData, &model.TaskManagementOverviewDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeThresholdConstraintsListData, &model.ThresholdConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeThresholdDescriptionListData, &model.ThresholdDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeThresholdListData, &model.ThresholdDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTierBoundaryDescriptionListData, &model.TierBoundaryDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTierBoundaryListData, &model.TierBoundaryDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTierDescriptionListData, &model.TierDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTierIncentiveRelationListData, &model.TierIncentiveRelationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTierListData, &model.TierDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeDistributorData, &model.TimeDistributorDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeDistributorEnquiryCall, &model.TimeDistributorEnquiryCallElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeInformationData, &model.TimeInformationDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimePrecisionData, &model.TimePrecisionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeSeriesConstraintsListData, &model.TimeSeriesConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeSeriesDescriptionListData, &model.TimeSeriesDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeSeriesListData, &model.TimeSeriesDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeTableConstraintsListData, &model.TimeTableConstraintsDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeTableDescriptionListData, &model.TimeTableDescriptionDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeTimeTableListData, &model.TimeTableDataElementsType{}) - assert.NotNil(suite.T(), result) - - result = addElementToFilter(filter, model.FunctionTypeUseCaseInformationListData, &model.UseCaseInformationDataElementsType{}) - assert.NotNil(suite.T(), result) -} - -func (suite *FctDataCmdSuite) Test_CreateCmd() { - result := createCmd(model.FunctionTypeActuatorLevelData, &model.ActuatorLevelDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeActuatorLevelDescriptionData, &model.ActuatorLevelDescriptionDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeActuatorSwitchData, &model.ActuatorSwitchDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeActuatorSwitchDescriptionData, &model.ActuatorSwitchDescriptionDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeAlarmListData, &model.AlarmListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeBillConstraintsListData, &model.BillConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeBillDescriptionListData, &model.BillDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeBillListData, &model.BillListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeBindingManagementDeleteCall, &model.BindingManagementDeleteCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeBindingManagementEntryListData, &model.BindingManagementEntryListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeBindingManagementRequestCall, &model.BindingManagementRequestCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeCommodityListData, &model.CommodityListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDataTunnelingCall, &model.DataTunnelingCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDeviceClassificationManufacturerData, &model.DeviceClassificationManufacturerDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDeviceClassificationUserData, &model.DeviceClassificationUserDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDeviceConfigurationKeyValueConstraintsListData, &model.DeviceConfigurationKeyValueConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, &model.DeviceConfigurationKeyValueDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDeviceConfigurationKeyValueListData, &model.DeviceConfigurationKeyValueListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDeviceDiagnosisHeartbeatData, &model.DeviceDiagnosisHeartbeatDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDeviceDiagnosisServiceData, &model.DeviceDiagnosisServiceDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDeviceDiagnosisStateData, &model.DeviceDiagnosisStateDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDirectControlActivityListData, &model.DirectControlActivityListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeDirectControlDescriptionData, &model.DirectControlDescriptionDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeElectricalConnectionParameterDescriptionListData, &model.ElectricalConnectionParameterDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeElectricalConnectionPermittedValueSetListData, &model.ElectricalConnectionPermittedValueSetListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeElectricalConnectionStateListData, &model.ElectricalConnectionStateListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeHvacOperationModeDescriptionListData, &model.HvacOperationModeDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeHvacOverrunDescriptionListData, &model.HvacOverrunDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeHvacOverrunListData, &model.HvacOverrunListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeHvacSystemFunctionDescriptionListData, &model.HvacSystemFunctionDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeHvacSystemFunctionListData, &model.HvacSystemFunctionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeHvacSystemFunctionOperationModeRelationListData, &model.HvacSystemFunctionOperationModeRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeHvacSystemFunctionPowerSequenceRelationListData, &model.HvacSystemFunctionPowerSequenceRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeHvacSystemFunctionSetPointRelationListData, &model.HvacSystemFunctionSetpointRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeIdentificationListData, &model.IdentificationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeIncentiveDescriptionListData, &model.IncentiveDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeIncentiveListData, &model.IncentiveListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeIncentiveTableConstraintsData, &model.IncentiveTableConstraintsDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeIncentiveTableData, &model.IncentiveTableDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeIncentiveTableDescriptionData, &model.IncentiveTableDescriptionDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeLoadControlEventListData, &model.LoadControlEventListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeLoadControlLimitConstraintsListData, &model.LoadControlLimitConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeLoadControlLimitDescriptionListData, &model.LoadControlLimitDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeLoadControlLimitListData, &model.LoadControlLimitListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeLoadControlNodeData, &model.LoadControlNodeDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeLoadControlStateListData, &model.LoadControlStateListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeMeasurementConstraintsListData, &model.MeasurementConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeMeasurementDescriptionListData, &model.MeasurementDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeMeasurementListData, &model.MeasurementListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeMeasurementThresholdRelationListData, &model.MeasurementThresholdRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeMessagingListData, &model.MessagingListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementAbortCall, &model.NetworkManagementAbortCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementAddNodeCall, &model.NetworkManagementAddNodeCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementDeviceDescriptionListData, &model.NetworkManagementDeviceDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementDiscoverCall, &model.NetworkManagementDiscoverCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementEntityDescriptionListData, &model.NetworkManagementEntityDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementFeatureDescriptionListData, &model.NetworkManagementFeatureDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementJoiningModeData, &model.NetworkManagementJoiningModeDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementModifyNodeCall, &model.NetworkManagementModifyNodeCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementProcessStateData, &model.NetworkManagementProcessStateDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementRemoveNodeCall, &model.NetworkManagementRemoveNodeCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementReportCandidateData, &model.NetworkManagementReportCandidateDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeNetworkManagementScanNetworkCall, &model.NetworkManagementScanNetworkCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeOperatingConstraintsDurationListData, &model.OperatingConstraintsDurationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeOperatingConstraintsInterruptListData, &model.OperatingConstraintsInterruptListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeOperatingConstraintsPowerDescriptionListData, &model.OperatingConstraintsPowerDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeOperatingConstraintsPowerLevelListData, &model.OperatingConstraintsPowerLevelListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeOperatingConstraintsPowerRangeListData, &model.OperatingConstraintsPowerRangeListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeOperatingConstraintsResumeImplicationListData, &model.OperatingConstraintsResumeImplicationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequenceAlternativesRelationListData, &model.PowerSequenceAlternativesRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequenceDescriptionListData, &model.PowerSequenceDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequenceNodeScheduleInformationData, &model.PowerSequenceNodeScheduleInformationDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequencePriceCalculationRequestCall, &model.PowerSequencePriceCalculationRequestCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequencePriceListData, &model.PowerSequencePriceListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequenceScheduleConfigurationRequestCall, &model.PowerSequenceScheduleConfigurationRequestCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequenceScheduleConstraintsListData, &model.PowerSequenceScheduleConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequenceScheduleListData, &model.PowerSequenceScheduleListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequenceSchedulePreferenceListData, &model.PowerSequenceSchedulePreferenceListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerSequenceStateListData, &model.PowerSequenceStateListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerTimeSlotScheduleConstraintsListData, &model.PowerTimeSlotScheduleConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerTimeSlotScheduleListData, &model.PowerTimeSlotScheduleListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypePowerTimeSlotValueListData, &model.PowerTimeSlotValueListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeResultData, &model.ResultDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSensingDescriptionData, &model.SensingDescriptionDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSensingListData, &model.SensingListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSetpointConstraintsListData, &model.SetpointConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSetpointDescriptionListData, &model.SetpointDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSetpointListData, &model.SetpointListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSmartEnergyManagementPsConfigurationRequestCall, &model.SmartEnergyManagementPsConfigurationRequestCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSmartEnergyManagementPsData, &model.SmartEnergyManagementPsDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSmartEnergyManagementPsPriceCalculationRequestCall, &model.SmartEnergyManagementPsPriceCalculationRequestCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSmartEnergyManagementPsPriceData, &model.SmartEnergyManagementPsPriceDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSpecificationVersionListData, &model.SpecificationVersionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSupplyConditionListData, &model.SupplyConditionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeSupplyConditionThresholdRelationListData, &model.SupplyConditionThresholdRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTariffBoundaryRelationListData, &model.TariffBoundaryRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTariffDescriptionListData, &model.TariffDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTariffListData, &model.TariffListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTariffOverallConstraintsData, &model.TariffOverallConstraintsDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTariffTierRelationListData, &model.TariffTierRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTaskManagementJobDescriptionListData, &model.TaskManagementJobDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTaskManagementJobListData, &model.TaskManagementJobListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTaskManagementJobRelationListData, &model.TaskManagementJobRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTaskManagementOverviewData, &model.TaskManagementOverviewDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeThresholdConstraintsListData, &model.ThresholdConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeThresholdDescriptionListData, &model.ThresholdDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeThresholdListData, &model.ThresholdListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTierBoundaryDescriptionListData, &model.TierBoundaryDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTierBoundaryListData, &model.TierBoundaryListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTierDescriptionListData, &model.TierDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTierIncentiveRelationListData, &model.TierIncentiveRelationListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTierListData, &model.TierListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeDistributorData, &model.TimeDistributorDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeDistributorEnquiryCall, &model.TimeDistributorEnquiryCallType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeInformationData, &model.TimeInformationDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimePrecisionData, &model.TimePrecisionDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeSeriesConstraintsListData, &model.TimeSeriesConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeSeriesDescriptionListData, &model.TimeSeriesDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeSeriesListData, &model.TimeSeriesListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeTableConstraintsListData, &model.TimeTableConstraintsListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeTableDescriptionListData, &model.TimeTableDescriptionListDataType{}) - assert.NotNil(suite.T(), result) - - result = createCmd(model.FunctionTypeTimeTableListData, &model.TimeTableListDataType{}) - assert.NotNil(suite.T(), result) -} diff --git a/spine/function_data_factory.go b/spine/function_data_factory.go deleted file mode 100644 index 2e0b1202..00000000 --- a/spine/function_data_factory.go +++ /dev/null @@ -1,318 +0,0 @@ -package spine - -import ( - "fmt" - - "github.com/enbility/eebus-go/spine/model" -) - -func CreateFunctionData[F any](featureType model.FeatureTypeType) []F { - // Some devices use generic for everything (e.g. Vaillant Arotherm heatpump) - // or for some things like the SMA HM 2.0 or Elli Wallbox, which uses Generic feature - // for Heartbeats, even though that should go into FeatureTypeTypeDeviceDiagnosis - // Hence we add everything to the Generic feature, as we don't know what might be needed - - var result []F - - if featureType == model.FeatureTypeTypeNodeManagement { - result = []F{ - createFunctionData[model.NodeManagementDestinationListDataType, F](model.FunctionTypeNodeManagementDestinationListData), - createFunctionData[model.NodeManagementDetailedDiscoveryDataType, F](model.FunctionTypeNodeManagementDetailedDiscoveryData), - createFunctionData[model.NodeManagementUseCaseDataType, F](model.FunctionTypeNodeManagementUseCaseData), - } - - return result - } - - if featureType == model.FeatureTypeTypeActuatorLevel || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.ActuatorLevelDataType, F](model.FunctionTypeActuatorLevelData), - createFunctionData[model.ActuatorLevelDescriptionDataType, F](model.FunctionTypeActuatorLevelDescriptionData), - }...) - } - - if featureType == model.FeatureTypeTypeActuatorSwitch || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.ActuatorSwitchDataType, F](model.FunctionTypeActuatorSwitchData), - createFunctionData[model.ActuatorSwitchDescriptionDataType, F](model.FunctionTypeActuatorSwitchDescriptionData), - }...) - } - - if featureType == model.FeatureTypeTypeAlarm || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.AlarmListDataType, F](model.FunctionTypeAlarmListData), - }...) - } - - if featureType == model.FeatureTypeTypeBill || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.BillDescriptionListDataType, F](model.FunctionTypeBillDescriptionListData), - createFunctionData[model.BillConstraintsListDataType, F](model.FunctionTypeBillConstraintsListData), - createFunctionData[model.BillListDataType, F](model.FunctionTypeBillListData), - }...) - } - - if featureType == model.FeatureTypeTypeDataTunneling || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.DataTunnelingCallType, F](model.FunctionTypeDataTunnelingCall), - }...) - } - - if featureType == model.FeatureTypeTypeDeviceClassification || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.DeviceClassificationManufacturerDataType, F](model.FunctionTypeDeviceClassificationManufacturerData), - createFunctionData[model.DeviceClassificationUserDataType, F](model.FunctionTypeDeviceClassificationUserData), - }...) - } - - if featureType == model.FeatureTypeTypeDeviceConfiguration || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.DeviceConfigurationKeyValueConstraintsListDataType, F](model.FunctionTypeDeviceConfigurationKeyValueConstraintsListData), - createFunctionData[model.DeviceConfigurationKeyValueDescriptionListDataType, F](model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData), - createFunctionData[model.DeviceConfigurationKeyValueListDataType, F](model.FunctionTypeDeviceConfigurationKeyValueListData), - }...) - } - - if featureType == model.FeatureTypeTypeDeviceDiagnosis || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.DeviceDiagnosisStateDataType, F](model.FunctionTypeDeviceDiagnosisStateData), - createFunctionData[model.DeviceDiagnosisHeartbeatDataType, F](model.FunctionTypeDeviceDiagnosisHeartbeatData), - createFunctionData[model.DeviceDiagnosisServiceDataType, F](model.FunctionTypeDeviceDiagnosisServiceData), - }...) - } - - if featureType == model.FeatureTypeTypeDirectControl || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.DirectControlActivityListDataType, F](model.FunctionTypeDirectControlActivityListData), - createFunctionData[model.DirectControlDescriptionDataType, F](model.FunctionTypeDirectControlDescriptionData), - }...) - } - - if featureType == model.FeatureTypeTypeElectricalConnection || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.ElectricalConnectionDescriptionListDataType, F](model.FunctionTypeElectricalConnectionDescriptionListData), - createFunctionData[model.ElectricalConnectionParameterDescriptionListDataType, F](model.FunctionTypeElectricalConnectionParameterDescriptionListData), - createFunctionData[model.ElectricalConnectionPermittedValueSetListDataType, F](model.FunctionTypeElectricalConnectionPermittedValueSetListData), - createFunctionData[model.ElectricalConnectionStateListDataType, F](model.FunctionTypeElectricalConnectionStateListData), - createFunctionData[model.ElectricalConnectionCharacteristicDataType, F](model.FunctionTypeElectricalConnectionCharacteristicListData), - createFunctionData[model.ElectricalConnectionCharacteristicListDataType, F](model.FunctionTypeElectricalConnectionCharacteristicListData), - }...) - } - - if featureType == model.FeatureTypeTypeHvac || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.HvacOperationModeDescriptionDataType, F](model.FunctionTypeHvacOperationModeDescriptionListData), - createFunctionData[model.HvacOverrunDescriptionListDataType, F](model.FunctionTypeHvacOverrunDescriptionListData), - createFunctionData[model.HvacOverrunListDataType, F](model.FunctionTypeHvacOverrunListData), - createFunctionData[model.HvacSystemFunctionDescriptionDataType, F](model.FunctionTypeHvacSystemFunctionDescriptionListData), - createFunctionData[model.HvacSystemFunctionListDataType, F](model.FunctionTypeHvacSystemFunctionListData), - createFunctionData[model.HvacSystemFunctionOperationModeRelationListDataType, F](model.FunctionTypeHvacSystemFunctionOperationModeRelationListData), - createFunctionData[model.HvacSystemFunctionPowerSequenceRelationListDataType, F](model.FunctionTypeHvacSystemFunctionPowerSequenceRelationListData), - createFunctionData[model.HvacSystemFunctionSetpointRelationListDataType, F](model.FunctionTypeHvacSystemFunctionSetPointRelationListData), - }...) - } - - if featureType == model.FeatureTypeTypeIdentification || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.IdentificationListDataType, F](model.FunctionTypeIdentificationListData), - createFunctionData[model.SessionIdentificationListDataType, F](model.FunctionTypeSessionIdentificationListData), - createFunctionData[model.SessionMeasurementRelationListDataType, F](model.FunctionTypeSessionMeasurementRelationListData), - }...) - } - - if featureType == model.FeatureTypeTypeIncentiveTable || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.IncentiveTableDescriptionDataType, F](model.FunctionTypeIncentiveTableDescriptionData), - createFunctionData[model.IncentiveTableConstraintsDataType, F](model.FunctionTypeIncentiveTableConstraintsData), - createFunctionData[model.IncentiveTableDataType, F](model.FunctionTypeIncentiveTableData), - }...) - } - - if featureType == model.FeatureTypeTypeLoadControl || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.LoadControlEventListDataType, F](model.FunctionTypeLoadControlEventListData), - createFunctionData[model.LoadControlLimitConstraintsListDataType, F](model.FunctionTypeLoadControlLimitConstraintsListData), - createFunctionData[model.LoadControlLimitDescriptionListDataType, F](model.FunctionTypeLoadControlLimitDescriptionListData), - createFunctionData[model.LoadControlLimitListDataType, F](model.FunctionTypeLoadControlLimitListData), - createFunctionData[model.LoadControlNodeDataType, F](model.FunctionTypeLoadControlNodeData), - createFunctionData[model.LoadControlStateListDataType, F](model.FunctionTypeLoadControlStateListData), - }...) - } - - if featureType == model.FeatureTypeTypeMeasurement || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.MeasurementListDataType, F](model.FunctionTypeMeasurementListData), - createFunctionData[model.MeasurementDescriptionListDataType, F](model.FunctionTypeMeasurementDescriptionListData), - createFunctionData[model.MeasurementConstraintsListDataType, F](model.FunctionTypeMeasurementConstraintsListData), - createFunctionData[model.MeasurementThresholdRelationListDataType, F](model.FunctionTypeMeasurementThresholdRelationListData), - createFunctionData[model.MeasurementSeriesListDataType, F](model.FunctionTypeMeasurementSeriesListData), - }...) - } - - if featureType == model.FeatureTypeTypeMessaging || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.MessagingListDataType, F](model.FunctionTypeMessagingListData), - }...) - } - - if featureType == model.FeatureTypeTypeNetworkManagement || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.NetworkManagementAbortCallType, F](model.FunctionTypeNetworkManagementAbortCall), - createFunctionData[model.NetworkManagementAddNodeCallType, F](model.FunctionTypeNetworkManagementAddNodeCall), - createFunctionData[model.NetworkManagementDeviceDescriptionListDataType, F](model.FunctionTypeNetworkManagementDeviceDescriptionListData), - createFunctionData[model.NetworkManagementDiscoverCallType, F](model.FunctionTypeNetworkManagementDiscoverCall), - createFunctionData[model.NetworkManagementEntityDescriptionListDataType, F](model.FunctionTypeNetworkManagementEntityDescriptionListData), - createFunctionData[model.NetworkManagementFeatureDescriptionListDataType, F](model.FunctionTypeNetworkManagementFeatureDescriptionListData), - createFunctionData[model.NetworkManagementJoiningModeDataType, F](model.FunctionTypeNetworkManagementJoiningModeData), - createFunctionData[model.NetworkManagementModifyNodeCallType, F](model.FunctionTypeNetworkManagementModifyNodeCall), - createFunctionData[model.NetworkManagementProcessStateDataType, F](model.FunctionTypeNetworkManagementProcessStateData), - createFunctionData[model.NetworkManagementRemoveNodeCallType, F](model.FunctionTypeNetworkManagementRemoveNodeCall), - createFunctionData[model.NetworkManagementReportCandidateDataType, F](model.FunctionTypeNetworkManagementReportCandidateData), - createFunctionData[model.NetworkManagementScanNetworkCallType, F](model.FunctionTypeNetworkManagementScanNetworkCall), - }...) - } - - if featureType == model.FeatureTypeTypeOperatingConstraints || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.OperatingConstraintsDurationListDataType, F](model.FunctionTypeOperatingConstraintsDurationListData), - createFunctionData[model.OperatingConstraintsInterruptListDataType, F](model.FunctionTypeOperatingConstraintsInterruptListData), - createFunctionData[model.OperatingConstraintsPowerDescriptionListDataType, F](model.FunctionTypeOperatingConstraintsPowerDescriptionListData), - createFunctionData[model.OperatingConstraintsPowerLevelListDataType, F](model.FunctionTypeOperatingConstraintsPowerLevelListData), - createFunctionData[model.OperatingConstraintsPowerRangeListDataType, F](model.FunctionTypeOperatingConstraintsPowerRangeListData), - createFunctionData[model.OperatingConstraintsResumeImplicationListDataType, F](model.FunctionTypeOperatingConstraintsResumeImplicationListData), - }...) - } - - if featureType == model.FeatureTypeTypePowerSequences || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.PowerSequenceAlternativesRelationListDataType, F](model.FunctionTypePowerSequenceAlternativesRelationListData), - createFunctionData[model.PowerSequenceDescriptionListDataType, F](model.FunctionTypePowerSequenceDescriptionListData), - createFunctionData[model.PowerSequenceNodeScheduleInformationDataType, F](model.FunctionTypePowerSequenceNodeScheduleInformationData), - createFunctionData[model.PowerSequencePriceCalculationRequestCallType, F](model.FunctionTypePowerSequencePriceCalculationRequestCall), - createFunctionData[model.PowerSequencePriceListDataType, F](model.FunctionTypePowerSequencePriceListData), - createFunctionData[model.PowerSequenceScheduleConfigurationRequestCallType, F](model.FunctionTypePowerSequenceScheduleConfigurationRequestCall), - createFunctionData[model.PowerSequenceScheduleConstraintsListDataType, F](model.FunctionTypePowerSequenceScheduleConstraintsListData), - createFunctionData[model.PowerSequenceScheduleListDataType, F](model.FunctionTypePowerSequenceScheduleListData), - createFunctionData[model.PowerSequenceSchedulePreferenceListDataType, F](model.FunctionTypePowerSequenceSchedulePreferenceListData), - createFunctionData[model.PowerSequenceStateListDataType, F](model.FunctionTypePowerSequenceStateListData), - createFunctionData[model.PowerTimeSlotScheduleConstraintsListDataType, F](model.FunctionTypePowerTimeSlotScheduleConstraintsListData), - createFunctionData[model.PowerTimeSlotScheduleListDataType, F](model.FunctionTypePowerTimeSlotScheduleListData), - createFunctionData[model.PowerTimeSlotValueListDataType, F](model.FunctionTypePowerTimeSlotValueListData), - }...) - } - - if featureType == model.FeatureTypeTypeSensing || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.SensingDescriptionDataType, F](model.FunctionTypeSensingDescriptionData), - createFunctionData[model.SensingListDataType, F](model.FunctionTypeSensingListData), - }...) - } - - if featureType == model.FeatureTypeTypeSetpoint || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.SetpointConstraintsListDataType, F](model.FunctionTypeSetpointConstraintsListData), - createFunctionData[model.SetpointDescriptionListDataType, F](model.FunctionTypeSetpointDescriptionListData), - createFunctionData[model.SetpointListDataType, F](model.FunctionTypeSetpointListData), - }...) - } - - if featureType == model.FeatureTypeTypeSmartEnergyManagementPs || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.SmartEnergyManagementPsConfigurationRequestCallType, F](model.FunctionTypeSmartEnergyManagementPsConfigurationRequestCall), - createFunctionData[model.SmartEnergyManagementPsDataType, F](model.FunctionTypeSmartEnergyManagementPsData), - createFunctionData[model.SmartEnergyManagementPsPriceCalculationRequestCallType, F](model.FunctionTypeSmartEnergyManagementPsPriceCalculationRequestCall), - createFunctionData[model.SmartEnergyManagementPsPriceDataType, F](model.FunctionTypeSmartEnergyManagementPsPriceData), - }...) - } - - if featureType == model.FeatureTypeTypeStateInformation || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.StateInformationListDataType, F](model.FunctionTypeStateInformationListData), - }...) - } - - if featureType == model.FeatureTypeTypeSupplyCondition || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.SupplyConditionDescriptionListDataType, F](model.FunctionTypeSupplyConditionDescriptionListData), - createFunctionData[model.SupplyConditionListDataType, F](model.FunctionTypeSupplyConditionListData), - createFunctionData[model.SupplyConditionThresholdRelationListDataType, F](model.FunctionTypeSupplyConditionThresholdRelationListData), - }...) - } - - if featureType == model.FeatureTypeTypeTariffInformation || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.IncentiveDescriptionListDataType, F](model.FunctionTypeIncentiveDescriptionListData), - createFunctionData[model.IncentiveListDataType, F](model.FunctionTypeIncentiveListData), - createFunctionData[model.TariffBoundaryRelationListDataType, F](model.FunctionTypeTariffBoundaryRelationListData), - createFunctionData[model.TariffDescriptionListDataType, F](model.FunctionTypeTariffDescriptionListData), - createFunctionData[model.TariffListDataType, F](model.FunctionTypeTariffListData), - createFunctionData[model.TariffOverallConstraintsDataType, F](model.FunctionTypeTariffOverallConstraintsData), - createFunctionData[model.TariffTierRelationListDataType, F](model.FunctionTypeTariffTierRelationListData), - createFunctionData[model.TierBoundaryDescriptionListDataType, F](model.FunctionTypeTierBoundaryDescriptionListData), - createFunctionData[model.TierBoundaryListDataType, F](model.FunctionTypeTierBoundaryListData), - createFunctionData[model.TierDescriptionListDataType, F](model.FunctionTypeTierDescriptionListData), - createFunctionData[model.TierIncentiveRelationListDataType, F](model.FunctionTypeTierIncentiveRelationListData), - createFunctionData[model.TierListDataType, F](model.FunctionTypeTierListData), - }...) - } - - if featureType == model.FeatureTypeTypeTaskManagement || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.TaskManagementJobDescriptionListDataType, F](model.FunctionTypeTaskManagementJobDescriptionListData), - createFunctionData[model.TaskManagementJobListDataType, F](model.FunctionTypeTaskManagementJobListData), - createFunctionData[model.TaskManagementJobRelationListDataType, F](model.FunctionTypeTaskManagementJobRelationListData), - createFunctionData[model.TaskManagementOverviewDataType, F](model.FunctionTypeTaskManagementOverviewData), - }...) - } - - if featureType == model.FeatureTypeTypeThreshold || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.ThresholdConstraintsListDataType, F](model.FunctionTypeThresholdConstraintsListData), - createFunctionData[model.ThresholdDescriptionListDataType, F](model.FunctionTypeThresholdDescriptionListData), - createFunctionData[model.ThresholdListDataType, F](model.FunctionTypeThresholdListData), - }...) - } - - if featureType == model.FeatureTypeTypeTimeInformation || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.TimeDistributorDataType, F](model.FunctionTypeTimeDistributorData), - createFunctionData[model.TimeDistributorEnquiryCallType, F](model.FunctionTypeTimeDistributorEnquiryCall), - createFunctionData[model.TimeInformationDataType, F](model.FunctionTypeTimeInformationData), - createFunctionData[model.TimePrecisionDataType, F](model.FunctionTypeTimePrecisionData), - }...) - } - - if featureType == model.FeatureTypeTypeTimeSeries || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.TimeSeriesDescriptionListDataType, F](model.FunctionTypeTimeSeriesDescriptionListData), - createFunctionData[model.TimeSeriesConstraintsListDataType, F](model.FunctionTypeTimeSeriesConstraintsListData), - createFunctionData[model.TimeSeriesListDataType, F](model.FunctionTypeTimeSeriesListData), - }...) - } - - if featureType == model.FeatureTypeTypeTimeTable || featureType == model.FeatureTypeTypeGeneric { - result = append(result, []F{ - createFunctionData[model.TimeTableConstraintsListDataType, F](model.FunctionTypeTimeTableConstraintsListData), - createFunctionData[model.TimeTableDescriptionListDataType, F](model.FunctionTypeTimeTableDescriptionListData), - createFunctionData[model.TimeTableListDataType, F](model.FunctionTypeTimeTableListData), - }...) - } - - if len(result) == 0 { - panic(fmt.Errorf("unknown featureType '%s'", featureType)) - } - - return result -} - -func createFunctionData[T any, F any](functionType model.FunctionType) F { - x := any(new(F)) - switch x.(type) { - case *FunctionDataCmd: - return any(NewFunctionDataCmd[T](functionType)).(F) - case *FunctionData: - return any(NewFunctionData[T](functionType)).(F) - default: - panic(fmt.Errorf("only FunctionData and FunctionDataCmd are supported")) - } -} diff --git a/spine/function_data_factory_test.go b/spine/function_data_factory_test.go deleted file mode 100644 index d51e4b19..00000000 --- a/spine/function_data_factory_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package spine - -import ( - "testing" - - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" -) - -func TestFunctionDataFactory_FunctionData(t *testing.T) { - result := CreateFunctionData[FunctionData](model.FeatureTypeTypeBill) - assert.Equal(t, 3, len(result)) - assert.IsType(t, &FunctionDataImpl[model.BillDescriptionListDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.BillConstraintsListDataType]{}, result[1]) - assert.IsType(t, &FunctionDataImpl[model.BillListDataType]{}, result[2]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeDeviceClassification) - assert.Equal(t, 2, len(result)) - assert.IsType(t, &FunctionDataImpl[model.DeviceClassificationManufacturerDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.DeviceClassificationUserDataType]{}, result[1]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeDeviceConfiguration) - assert.Equal(t, 3, len(result)) - assert.IsType(t, &FunctionDataImpl[model.DeviceConfigurationKeyValueConstraintsListDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.DeviceConfigurationKeyValueDescriptionListDataType]{}, result[1]) - assert.IsType(t, &FunctionDataImpl[model.DeviceConfigurationKeyValueListDataType]{}, result[2]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeDeviceDiagnosis) - assert.Equal(t, 3, len(result)) - assert.IsType(t, &FunctionDataImpl[model.DeviceDiagnosisStateDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.DeviceDiagnosisHeartbeatDataType]{}, result[1]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeElectricalConnection) - assert.Equal(t, 6, len(result)) - assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionDescriptionListDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionParameterDescriptionListDataType]{}, result[1]) - assert.IsType(t, &FunctionDataImpl[model.ElectricalConnectionPermittedValueSetListDataType]{}, result[2]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeHvac) - assert.Equal(t, 8, len(result)) - assert.IsType(t, &FunctionDataImpl[model.HvacOperationModeDescriptionDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.HvacOverrunDescriptionListDataType]{}, result[1]) - assert.IsType(t, &FunctionDataImpl[model.HvacOverrunListDataType]{}, result[2]) - assert.IsType(t, &FunctionDataImpl[model.HvacSystemFunctionDescriptionDataType]{}, result[3]) - assert.IsType(t, &FunctionDataImpl[model.HvacSystemFunctionListDataType]{}, result[4]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeIdentification) - assert.Equal(t, 3, len(result)) - assert.IsType(t, &FunctionDataImpl[model.IdentificationListDataType]{}, result[0]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeIncentiveTable) - assert.Equal(t, 3, len(result)) - assert.IsType(t, &FunctionDataImpl[model.IncentiveTableDescriptionDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.IncentiveTableConstraintsDataType]{}, result[1]) - assert.IsType(t, &FunctionDataImpl[model.IncentiveTableDataType]{}, result[2]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeLoadControl) - assert.Equal(t, 6, len(result)) - assert.IsType(t, &FunctionDataImpl[model.LoadControlEventListDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.LoadControlLimitConstraintsListDataType]{}, result[1]) - assert.IsType(t, &FunctionDataImpl[model.LoadControlLimitDescriptionListDataType]{}, result[2]) - assert.IsType(t, &FunctionDataImpl[model.LoadControlLimitListDataType]{}, result[3]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeMeasurement) - assert.Equal(t, 5, len(result)) - assert.IsType(t, &FunctionDataImpl[model.MeasurementListDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.MeasurementDescriptionListDataType]{}, result[1]) - assert.IsType(t, &FunctionDataImpl[model.MeasurementConstraintsListDataType]{}, result[2]) - assert.IsType(t, &FunctionDataImpl[model.MeasurementThresholdRelationListDataType]{}, result[3]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeTimeSeries) - assert.Equal(t, 3, len(result)) - assert.IsType(t, &FunctionDataImpl[model.TimeSeriesDescriptionListDataType]{}, result[0]) - assert.IsType(t, &FunctionDataImpl[model.TimeSeriesConstraintsListDataType]{}, result[1]) - assert.IsType(t, &FunctionDataImpl[model.TimeSeriesListDataType]{}, result[2]) - - result = CreateFunctionData[FunctionData](model.FeatureTypeTypeGeneric) - assert.Equal(t, 124, len(result)) -} - -func TestFunctionDataFactory_FunctionDataCmd(t *testing.T) { - result := CreateFunctionData[FunctionDataCmd](model.FeatureTypeTypeDeviceClassification) - assert.Equal(t, 2, len(result)) - assert.IsType(t, &FunctionDataCmdImpl[model.DeviceClassificationManufacturerDataType]{}, result[0]) - assert.IsType(t, &FunctionDataCmdImpl[model.DeviceClassificationUserDataType]{}, result[1]) -} - -func TestFunctionDataFactory_NodeMgmtFeatureType(t *testing.T) { - result := CreateFunctionData[FunctionDataCmd](model.FeatureTypeTypeNodeManagement) - assert.Equal(t, 3, len(result)) -} - -func TestFunctionDataFactory_unknownFunctionDataType(t *testing.T) { - assert.PanicsWithError(t, "only FunctionData and FunctionDataCmd are supported", - func() { CreateFunctionData[int](model.FeatureTypeTypeDeviceClassification) }) -} diff --git a/spine/function_data_test.go b/spine/function_data_test.go deleted file mode 100644 index c2475484..00000000 --- a/spine/function_data_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package spine - -import ( - "testing" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestFunctionData_UpdateData(t *testing.T) { - newData := &model.DeviceClassificationManufacturerDataType{ - DeviceName: util.Ptr(model.DeviceClassificationStringType("device name")), - } - functionType := model.FunctionTypeDeviceClassificationManufacturerData - sut := NewFunctionData[model.DeviceClassificationManufacturerDataType](functionType) - sut.UpdateData(newData, nil, nil) - getData := sut.DataCopy() - - assert.Equal(t, newData.DeviceName, getData.DeviceName) - assert.Equal(t, functionType, sut.Function()) - - // another update should not be reflected in the first dataset - newData = &model.DeviceClassificationManufacturerDataType{ - DeviceName: util.Ptr(model.DeviceClassificationStringType("new device name")), - } - sut.UpdateData(newData, nil, nil) - getNewData := sut.DataCopy() - - assert.Equal(t, newData.DeviceName, getNewData.DeviceName) - assert.NotEqual(t, getData.DeviceName, getNewData.DeviceName) - assert.Equal(t, functionType, sut.Function()) -} - -func TestFunctionData_UpdateDataPartial(t *testing.T) { - newData := &model.ElectricalConnectionPermittedValueSetListDataType{ - ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(1)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)), - PermittedValueSet: []model.ScaledNumberSetType{ - { - Range: []model.ScaledNumberRangeType{ - { - Min: &model.ScaledNumberType{ - Number: util.Ptr(model.NumberType(6)), - Scale: util.Ptr(model.ScaleType(0)), - }, - }, - }, - }, - }, - }, - }, - } - functionType := model.FunctionTypeElectricalConnectionPermittedValueSetListData - sut := NewFunctionData[model.ElectricalConnectionPermittedValueSetListDataType](functionType) - - err := sut.UpdateData(newData, &model.FilterType{CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}}, nil) - if assert.Nil(t, err) { - getData := sut.DataCopy() - assert.Equal(t, 1, len(getData.ElectricalConnectionPermittedValueSetData)) - } -} - -func TestFunctionData_UpdateDataPartial_Supported(t *testing.T) { - newData := &model.HvacOverrunListDataType{ - HvacOverrunData: []model.HvacOverrunDataType{ - { - OverrunId: util.Ptr(model.HvacOverrunIdType(1)), - }, - }, - } - functionType := model.FunctionTypeHvacOverrunListData - sut := NewFunctionData[model.HvacOverrunListDataType](functionType) - - err := sut.UpdateData(newData, &model.FilterType{CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}}, nil) - assert.Nil(t, err) -} diff --git a/spine/heartbeat_manager.go b/spine/heartbeat_manager.go deleted file mode 100644 index c5926e4f..00000000 --- a/spine/heartbeat_manager.go +++ /dev/null @@ -1,147 +0,0 @@ -package spine - -import ( - "errors" - "sync" - "sync/atomic" - "time" - - "github.com/enbility/eebus-go/spine/model" -) - -type HeartbeatManagerImpl struct { - localDevice DeviceLocal - localEntity *EntityLocalImpl - localFeature FeatureLocal - - heartBeatNum uint64 // see https://github.com/golang/go/issues/11891 - stopHeartbeatC chan struct{} - stopMux sync.Mutex - - subscriptionManager SubscriptionManager - heartBeatTimeout *model.DurationType -} - -// Create a new Heartbeat Manager which handles sending of heartbeats -func NewHeartbeatManager(localDevice DeviceLocal, subscriptionManager SubscriptionManager, timeout time.Duration) HeartbeatManager { - h := &HeartbeatManagerImpl{ - localDevice: localDevice, - subscriptionManager: subscriptionManager, - heartBeatTimeout: model.NewDurationType(timeout), - } - - return h -} - -func (c *HeartbeatManagerImpl) IsHeartbeatRunning() bool { - c.stopMux.Lock() - defer c.stopMux.Unlock() - - if c.stopHeartbeatC != nil && !c.isHeartbeatClosed() { - return true - } - - return false -} - -// check if there are any heartbeat subscriptions left, otherwise stop creating new ones -// or start creating heartbeats again if needed -func (c *HeartbeatManagerImpl) UpdateHeartbeatOnSubscriptions() { - if c.localEntity == nil { - return - } - - featureAddr := c.localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - if featureAddr == nil { - return - } - - subscriptions := c.subscriptionManager.SubscriptionsOnFeature(*featureAddr.Address()) - if len(subscriptions) == 0 { - // stop creating heartbeats - c.StopHeartbeat() - } else if !c.IsHeartbeatRunning() { - // resume creating heartbeats - _ = c.StartHeartbeat() - } -} - -func (c *HeartbeatManagerImpl) SetLocalFeature(entity *EntityLocalImpl, feature FeatureLocal) { - c.localEntity = entity - c.localFeature = feature -} - -// Start setting heartbeat data -// Make sure the a required FeatureTypeTypeDeviceDiagnosis with the role server is present -// otherwise this will end with an error -// Note: Remote features need to have a subscription to get notifications -func (c *HeartbeatManagerImpl) StartHeartbeat() error { - if c.localEntity == nil { - return errors.New("unknown entity") - } - - timeout, err := c.heartBeatTimeout.GetTimeDuration() - if err != nil { - return err - } - - // stop an already running heartbeat - c.StopHeartbeat() - - c.stopHeartbeatC = make(chan struct{}) - - go c.updateHearbeatData(c.stopHeartbeatC, timeout) - - return nil -} - -// Stop updating heartbeat data -// Note: No active subscribers will get any further notifications! -func (c *HeartbeatManagerImpl) StopHeartbeat() { - if c.IsHeartbeatRunning() { - close(c.stopHeartbeatC) - } -} - -func (c *HeartbeatManagerImpl) heartbeatData(t time.Time, counter *uint64) *model.DeviceDiagnosisHeartbeatDataType { - timestamp := t.UTC().Format(time.RFC3339) - - return &model.DeviceDiagnosisHeartbeatDataType{ - Timestamp: ×tamp, - HeartbeatCounter: counter, - HeartbeatTimeout: c.heartBeatTimeout, - } -} - -func (c *HeartbeatManagerImpl) updateHearbeatData(stopC chan struct{}, d time.Duration) { - ticker := time.NewTicker(d) - for { - select { - case <-ticker.C: - - heartbeatData := c.heartbeatData(time.Now(), c.heartBeatCounter()) - - // updating the data will automatically notify all subscribed remote features - c.localFeature.SetData(model.FunctionTypeDeviceDiagnosisHeartbeatData, heartbeatData) - - case <-stopC: - return - } - } -} - -func (c *HeartbeatManagerImpl) isHeartbeatClosed() bool { - select { - case <-c.stopHeartbeatC: - return true - default: - } - - return false -} - -// TODO heartBeatCounter should be global on CEM level, not on connection level -func (c *HeartbeatManagerImpl) heartBeatCounter() *uint64 { - i := atomic.AddUint64(&c.heartBeatNum, 1) - return &i -} diff --git a/spine/heartbeat_manager_test.go b/spine/heartbeat_manager_test.go deleted file mode 100644 index 3619fca5..00000000 --- a/spine/heartbeat_manager_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestHeartbeatManagerSuite(t *testing.T) { - suite.Run(t, new(HeartBeatManagerSuite)) -} - -type HeartBeatManagerSuite struct { - suite.Suite - - localDevice DeviceLocal - remoteDevice DeviceRemote - sut HeartbeatManager -} - -func (suite *HeartBeatManagerSuite) WriteSpineMessage([]byte) {} - -func (suite *HeartBeatManagerSuite) SetupSuite() { - suite.localDevice = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - - ski := "test" - sender := NewSender(suite) - suite.remoteDevice = NewDeviceRemoteImpl(suite.localDevice, ski, sender) - - _ = suite.localDevice.SetupRemoteDevice(ski, suite) - - suite.sut = suite.localDevice.HeartbeatManager() -} - -func (suite *HeartBeatManagerSuite) Test_HeartbeatFailure() { - err := suite.sut.StartHeartbeat() - assert.NotNil(suite.T(), err) -} - -func (suite *HeartBeatManagerSuite) Test_HeartbeatSuccess() { - entity := NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) - suite.localDevice.AddEntity(entity) - - localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - entity.AddFeature(localFeature) - - remoteEntity := NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) - suite.remoteDevice.AddEntity(remoteEntity) - - remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - remoteEntity.AddFeature(remoteFeature) - - subscrRequest := &model.SubscriptionManagementRequestCallType{ - ClientAddress: remoteFeature.Address(), - ServerAddress: localFeature.Address(), - ServerFeatureType: util.Ptr(model.FeatureTypeTypeDeviceDiagnosis), - } - - datagram := model.DatagramType{ - Header: model.HeaderType{ - SpecificationVersion: &SpecificationVersion, - AddressSource: &model.FeatureAddressType{ - Device: suite.remoteDevice.Address(), - Entity: []model.AddressEntityType{0}, - Feature: util.Ptr(model.AddressFeatureType(0)), - }, - AddressDestination: &model.FeatureAddressType{ - Device: suite.localDevice.Address(), - Entity: []model.AddressEntityType{0}, - Feature: util.Ptr(model.AddressFeatureType(0)), - }, - MsgCounter: util.Ptr(model.MsgCounterType(1000)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeCall), - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{ - { - NodeManagementSubscriptionRequestCall: &model.NodeManagementSubscriptionRequestCallType{ - SubscriptionRequest: subscrRequest, - }, - }, - }, - }, - } - err := suite.localDevice.ProcessCmd(datagram, suite.remoteDevice) - assert.Nil(suite.T(), err) - - data := localFeature.DataCopy(model.FunctionTypeDeviceDiagnosisHeartbeatData) - assert.Nil(suite.T(), data) - - running := suite.sut.IsHeartbeatRunning() - assert.Equal(suite.T(), true, running) - - err = suite.sut.StartHeartbeat() - assert.Nil(suite.T(), err) - - time.Sleep(time.Second * 5) - - running = suite.sut.IsHeartbeatRunning() - assert.Equal(suite.T(), true, running) - - data = localFeature.DataCopy(model.FunctionTypeDeviceDiagnosisHeartbeatData) - assert.NotNil(suite.T(), data) - - fctData := data.(*model.DeviceDiagnosisHeartbeatDataType) - var resultCounter uint64 = 1 - assert.Equal(suite.T(), resultCounter, *fctData.HeartbeatCounter) - resultTimeout, err := fctData.HeartbeatTimeout.GetTimeDuration() - assert.Nil(suite.T(), err) - assert.Equal(suite.T(), time.Second*4, resultTimeout) - - subscrDelRequest := &model.SubscriptionManagementDeleteCallType{ - ClientAddress: remoteFeature.Address(), - ServerAddress: localFeature.Address(), - } - - datagram.Payload = model.PayloadType{ - Cmd: []model.CmdType{ - { - NodeManagementSubscriptionDeleteCall: &model.NodeManagementSubscriptionDeleteCallType{ - SubscriptionDelete: subscrDelRequest, - }, - }, - }, - } - - err = suite.localDevice.ProcessCmd(datagram, suite.remoteDevice) - assert.Nil(suite.T(), err) - - isHeartbeatRunning := suite.sut.IsHeartbeatRunning() - assert.Equal(suite.T(), false, isHeartbeatRunning) - - suite.sut.StopHeartbeat() -} diff --git a/spine/helper_test.go b/spine/helper_test.go deleted file mode 100644 index 06ad3c72..00000000 --- a/spine/helper_test.go +++ /dev/null @@ -1,196 +0,0 @@ -package spine - -import ( - "encoding/json" - "fmt" - "os" - "sync" - "testing" - "time" - - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/stretchr/testify/assert" -) - -const ( - wallbox_detaileddiscoverydata_recv_reply_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_reply.json" - wallbox_detaileddiscoverydata_recv_notify_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_notify.json" -) - -type WriteMessageHandler struct { - sentMessages [][]byte - - mux sync.Mutex -} - -var _ ship.SpineDataConnection = (*WriteMessageHandler)(nil) - -func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { - t.mux.Lock() - defer t.mux.Unlock() - - t.sentMessages = append(t.sentMessages, message) -} - -func (t *WriteMessageHandler) LastMessage() []byte { - t.mux.Lock() - defer t.mux.Unlock() - - if len(t.sentMessages) == 0 { - return nil - } - - return t.sentMessages[len(t.sentMessages)-1] -} - -func (t *WriteMessageHandler) MessageWithReference(msgCounterReference *model.MsgCounterType) []byte { - t.mux.Lock() - defer t.mux.Unlock() - - var datagram model.Datagram - - for _, msg := range t.sentMessages { - if err := json.Unmarshal(msg, &datagram); err != nil { - return nil - } - if datagram.Datagram.Header.MsgCounterReference == nil { - continue - } - if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { - continue - } - if datagram.Datagram.Payload.Cmd[0].ResultData != nil { - continue - } - - return msg - } - - return nil -} - -func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.MsgCounterType) []byte { - t.mux.Lock() - defer t.mux.Unlock() - - var datagram model.Datagram - - for _, msg := range t.sentMessages { - if err := json.Unmarshal(msg, &datagram); err != nil { - return nil - } - if datagram.Datagram.Header.MsgCounterReference == nil { - continue - } - if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { - continue - } - if datagram.Datagram.Payload.Cmd[0].ResultData == nil { - continue - } - - return msg - } - - return nil -} - -func loadFileData(t *testing.T, fileName string) []byte { - fileData, err := os.ReadFile(fileName) - if err != nil { - t.Fatal(err) - } - - return fileData -} - -func checkSentData(t *testing.T, sendBytes []byte, msgSendFilePrefix string) { - msgSendExpectedBytes, err := os.ReadFile(msgSendFilePrefix + "_expected.json") - if err != nil { - t.Fatal(err) - } - - msgSendActualFileName := msgSendFilePrefix + "_actual.json" - equal := jsonDatagramEqual(t, msgSendExpectedBytes, sendBytes) - if !equal { - saveJsonToFile(t, sendBytes, msgSendActualFileName) - } - assert.Truef(t, equal, "Assert equal failed! Check '%s' ", msgSendActualFileName) -} - -func jsonDatagramEqual(t *testing.T, expectedJson, actualJson []byte) bool { - var actualDatagram model.Datagram - if err := json.Unmarshal(actualJson, &actualDatagram); err != nil { - t.Fatal(err) - } - var expectedDatagram model.Datagram - if err := json.Unmarshal(expectedJson, &expectedDatagram); err != nil { - t.Fatal(err) - } - - less := func(a, b model.FunctionPropertyType) bool { return string(*a.Function) < string(*b.Function) } - return cmp.Equal(expectedDatagram, actualDatagram, cmpopts.SortSlices(less)) -} - -func saveJsonToFile(t *testing.T, data json.RawMessage, fileName string) { - jsonIndent, err := json.MarshalIndent(data, "", " ") - if err != nil { - t.Fatal(err) - } - err = os.WriteFile(fileName, jsonIndent, os.ModePerm) - if err != nil { - t.Fatal(err) - } -} - -func waitForAck(t *testing.T, msgCounterReference *model.MsgCounterType, writeHandler *WriteMessageHandler) { - var datagram model.Datagram - - msg := writeHandler.ResultWithReference(msgCounterReference) - if msg == nil { - t.Fatal("acknowledge message was not sent!!") - } - - if err := json.Unmarshal(msg, &datagram); err != nil { - t.Fatal(err) - } - - cmd := datagram.Datagram.Payload.Cmd[0] - if cmd.ResultData != nil { - if cmd.ResultData.ErrorNumber != nil && uint(*cmd.ResultData.ErrorNumber) != uint(model.ErrorNumberTypeNoError) { - t.Fatal(fmt.Errorf("error '%d' result data received", uint(*cmd.ResultData.ErrorNumber))) - } - } -} - -func createLocalDeviceAndFeature(entityId uint, featureType model.FeatureTypeType) (FeatureLocal, FeatureLocal) { - localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localDevice.address = util.Ptr(model.AddressDeviceType("Address")) - localEntity := NewEntityLocalImpl(localDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) - localDevice.AddEntity(localEntity) - localFeature := NewFeatureLocalImpl(localEntity.NextFeatureId(), localEntity, featureType, model.RoleTypeClient) - localEntity.AddFeature(localFeature) - localServerFeature := NewFeatureLocalImpl(localEntity.NextFeatureId(), localEntity, featureType, model.RoleTypeServer) - localEntity.AddFeature(localServerFeature) - - return localFeature, localServerFeature -} - -func createRemoteDeviceAndFeature(entityId uint, featureType model.FeatureTypeType, sender Sender) (FeatureRemote, FeatureRemote) { - localDevice := NewDeviceLocalImpl("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - - remoteDevice := NewDeviceRemoteImpl(localDevice, "ski", sender) - remoteDevice.address = util.Ptr(model.AddressDeviceType("Address")) - remoteEntity := NewEntityRemoteImpl(remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) - remoteDevice.AddEntity(remoteEntity) - remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, model.RoleTypeClient) - remoteEntity.AddFeature(remoteFeature) - remoteServerFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, featureType, model.RoleTypeServer) - remoteEntity.AddFeature(remoteServerFeature) - - return remoteFeature, remoteServerFeature -} diff --git a/spine/message.go b/spine/message.go deleted file mode 100644 index 4f6b9c05..00000000 --- a/spine/message.go +++ /dev/null @@ -1,23 +0,0 @@ -package spine - -import "github.com/enbility/eebus-go/spine/model" - -type Message struct { - RequestHeader *model.HeaderType - CmdClassifier model.CmdClassifierType - Cmd model.CmdType - FilterPartial *model.FilterType - FilterDelete *model.FilterType - FeatureRemote FeatureRemote - EntityRemote EntityRemote - DeviceRemote DeviceRemote -} - -type ResultMessage struct { - MsgCounterReference model.MsgCounterType // required - Result *model.ResultDataType // required, may not be nil - FeatureLocal FeatureLocal // required, may not be nil - FeatureRemote FeatureRemote // required, may not be nil - EntityRemote EntityRemote // required, may not be nil - DeviceRemote DeviceRemote // required, may not be nil -} diff --git a/spine/mocks/Sender.go b/spine/mocks/Sender.go deleted file mode 100644 index 18498168..00000000 --- a/spine/mocks/Sender.go +++ /dev/null @@ -1,319 +0,0 @@ -// Code generated by mockery v2.39.1. DO NOT EDIT. - -package mocks - -import ( - model "github.com/enbility/eebus-go/spine/model" - mock "github.com/stretchr/testify/mock" -) - -// Sender is an autogenerated mock type for the Sender type -type Sender struct { - mock.Mock -} - -// Bind provides a mock function with given fields: senderAddress, destinationAddress, serverFeatureType -func (_m *Sender) Bind(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) { - ret := _m.Called(senderAddress, destinationAddress, serverFeatureType) - - if len(ret) == 0 { - panic("no return value specified for Bind") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) (*model.MsgCounterType, error)); ok { - return rf(senderAddress, destinationAddress, serverFeatureType) - } - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) *model.MsgCounterType); ok { - r0 = rf(senderAddress, destinationAddress, serverFeatureType) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) error); ok { - r1 = rf(senderAddress, destinationAddress, serverFeatureType) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DatagramForMsgCounter provides a mock function with given fields: msgCounter -func (_m *Sender) DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) { - ret := _m.Called(msgCounter) - - if len(ret) == 0 { - panic("no return value specified for DatagramForMsgCounter") - } - - var r0 model.DatagramType - var r1 error - if rf, ok := ret.Get(0).(func(model.MsgCounterType) (model.DatagramType, error)); ok { - return rf(msgCounter) - } - if rf, ok := ret.Get(0).(func(model.MsgCounterType) model.DatagramType); ok { - r0 = rf(msgCounter) - } else { - r0 = ret.Get(0).(model.DatagramType) - } - - if rf, ok := ret.Get(1).(func(model.MsgCounterType) error); ok { - r1 = rf(msgCounter) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Notify provides a mock function with given fields: senderAddress, destinationAddress, cmd -func (_m *Sender) Notify(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) { - ret := _m.Called(senderAddress, destinationAddress, cmd) - - if len(ret) == 0 { - panic("no return value specified for Notify") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) (*model.MsgCounterType, error)); ok { - return rf(senderAddress, destinationAddress, cmd) - } - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) *model.MsgCounterType); ok { - r0 = rf(senderAddress, destinationAddress, cmd) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) error); ok { - r1 = rf(senderAddress, destinationAddress, cmd) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Reply provides a mock function with given fields: requestHeader, senderAddress, cmd -func (_m *Sender) Reply(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, cmd model.CmdType) error { - ret := _m.Called(requestHeader, senderAddress, cmd) - - if len(ret) == 0 { - panic("no return value specified for Reply") - } - - var r0 error - if rf, ok := ret.Get(0).(func(*model.HeaderType, *model.FeatureAddressType, model.CmdType) error); ok { - r0 = rf(requestHeader, senderAddress, cmd) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Request provides a mock function with given fields: cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd -func (_m *Sender) Request(cmdClassifier model.CmdClassifierType, senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, ackRequest bool, cmd []model.CmdType) (*model.MsgCounterType, error) { - ret := _m.Called(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) - - if len(ret) == 0 { - panic("no return value specified for Request") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func(model.CmdClassifierType, *model.FeatureAddressType, *model.FeatureAddressType, bool, []model.CmdType) (*model.MsgCounterType, error)); ok { - return rf(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) - } - if rf, ok := ret.Get(0).(func(model.CmdClassifierType, *model.FeatureAddressType, *model.FeatureAddressType, bool, []model.CmdType) *model.MsgCounterType); ok { - r0 = rf(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func(model.CmdClassifierType, *model.FeatureAddressType, *model.FeatureAddressType, bool, []model.CmdType) error); ok { - r1 = rf(cmdClassifier, senderAddress, destinationAddress, ackRequest, cmd) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ResultError provides a mock function with given fields: requestHeader, senderAddress, err -func (_m *Sender) ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error { - ret := _m.Called(requestHeader, senderAddress, err) - - if len(ret) == 0 { - panic("no return value specified for ResultError") - } - - var r0 error - if rf, ok := ret.Get(0).(func(*model.HeaderType, *model.FeatureAddressType, *model.ErrorType) error); ok { - r0 = rf(requestHeader, senderAddress, err) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ResultSuccess provides a mock function with given fields: requestHeader, senderAddress -func (_m *Sender) ResultSuccess(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType) error { - ret := _m.Called(requestHeader, senderAddress) - - if len(ret) == 0 { - panic("no return value specified for ResultSuccess") - } - - var r0 error - if rf, ok := ret.Get(0).(func(*model.HeaderType, *model.FeatureAddressType) error); ok { - r0 = rf(requestHeader, senderAddress) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Subscribe provides a mock function with given fields: senderAddress, destinationAddress, serverFeatureType -func (_m *Sender) Subscribe(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) { - ret := _m.Called(senderAddress, destinationAddress, serverFeatureType) - - if len(ret) == 0 { - panic("no return value specified for Subscribe") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) (*model.MsgCounterType, error)); ok { - return rf(senderAddress, destinationAddress, serverFeatureType) - } - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) *model.MsgCounterType); ok { - r0 = rf(senderAddress, destinationAddress, serverFeatureType) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.FeatureTypeType) error); ok { - r1 = rf(senderAddress, destinationAddress, serverFeatureType) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Unbind provides a mock function with given fields: senderAddress, destinationAddress -func (_m *Sender) Unbind(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) { - ret := _m.Called(senderAddress, destinationAddress) - - if len(ret) == 0 { - panic("no return value specified for Unbind") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType) (*model.MsgCounterType, error)); ok { - return rf(senderAddress, destinationAddress) - } - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType) *model.MsgCounterType); ok { - r0 = rf(senderAddress, destinationAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType) error); ok { - r1 = rf(senderAddress, destinationAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Unsubscribe provides a mock function with given fields: senderAddress, destinationAddress -func (_m *Sender) Unsubscribe(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) { - ret := _m.Called(senderAddress, destinationAddress) - - if len(ret) == 0 { - panic("no return value specified for Unsubscribe") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType) (*model.MsgCounterType, error)); ok { - return rf(senderAddress, destinationAddress) - } - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType) *model.MsgCounterType); ok { - r0 = rf(senderAddress, destinationAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType) error); ok { - r1 = rf(senderAddress, destinationAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Write provides a mock function with given fields: senderAddress, destinationAddress, cmd -func (_m *Sender) Write(senderAddress *model.FeatureAddressType, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) { - ret := _m.Called(senderAddress, destinationAddress, cmd) - - if len(ret) == 0 { - panic("no return value specified for Write") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) (*model.MsgCounterType, error)); ok { - return rf(senderAddress, destinationAddress, cmd) - } - if rf, ok := ret.Get(0).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) *model.MsgCounterType); ok { - r0 = rf(senderAddress, destinationAddress, cmd) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func(*model.FeatureAddressType, *model.FeatureAddressType, model.CmdType) error); ok { - r1 = rf(senderAddress, destinationAddress, cmd) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewSender creates a new instance of Sender. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewSender(t interface { - mock.TestingT - Cleanup(func()) -}) *Sender { - mock := &Sender{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/spine/mocks/SpineDataConnection.go b/spine/mocks/SpineDataConnection.go deleted file mode 100644 index 5be34a68..00000000 --- a/spine/mocks/SpineDataConnection.go +++ /dev/null @@ -1,29 +0,0 @@ -// Code generated by mockery v2.39.1. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// SpineDataConnection is an autogenerated mock type for the SpineDataConnection type -type SpineDataConnection struct { - mock.Mock -} - -// WriteSpineMessage provides a mock function with given fields: message -func (_m *SpineDataConnection) WriteSpineMessage(message []byte) { - _m.Called(message) -} - -// NewSpineDataConnection creates a new instance of SpineDataConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewSpineDataConnection(t interface { - mock.TestingT - Cleanup(func()) -}) *SpineDataConnection { - mock := &SpineDataConnection{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/spine/model/actuatorlevel.go b/spine/model/actuatorlevel.go deleted file mode 100644 index 64e4077d..00000000 --- a/spine/model/actuatorlevel.go +++ /dev/null @@ -1,36 +0,0 @@ -package model - -type ActuatorLevelFctType string - -const ( - ActuatorLevelFctTypeStart ActuatorLevelFctType = "start" - ActuatorLevelFctTypeUp ActuatorLevelFctType = "up" - ActuatorLevelFctTypeDown ActuatorLevelFctType = "down" - ActuatorLevelFctTypeStop ActuatorLevelFctType = "stop" - ActuatorLevelFctTypePercentageAbsolute ActuatorLevelFctType = "percentageAbsolute" - ActuatorLevelFctTypePercentageRelative ActuatorLevelFctType = "percentageRelative" - ActuatorLevelFctTypeAbsolut ActuatorLevelFctType = "absolut" - ActuatorLevelFctTypeRelative ActuatorLevelFctType = "relative" -) - -type ActuatorLevelDataType struct { - Function *ActuatorLevelFctType `json:"function,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` -} - -type ActuatorLevelDataElementsType struct { - Function *ElementTagType `json:"function,omitempty"` - Value *ElementTagType `json:"value,omitempty"` -} - -type ActuatorLevelDescriptionDataType struct { - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` - LevelDefaultUnit *UnitOfMeasurementType `json:"levelDefaultUnit,omitempty"` -} - -type ActuatorLevelDescriptionDataElementsType struct { - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` - LevelDefaultUnit *ElementTagType `json:"levelDefaultUnit,omitempty"` -} diff --git a/spine/model/actuatorswitch.go b/spine/model/actuatorswitch.go deleted file mode 100644 index cd76d39d..00000000 --- a/spine/model/actuatorswitch.go +++ /dev/null @@ -1,27 +0,0 @@ -package model - -type ActuatorSwitchFctType string - -const ( - ActuatorSwitchFctTypeOn ActuatorSwitchFctType = "on" - ActuatorSwitchFctTypeOff ActuatorSwitchFctType = "off" - ActuatorSwitchFctTypeToggle ActuatorSwitchFctType = "toggle" -) - -type ActuatorSwitchDataType struct { - Function *ActuatorSwitchFctType `json:"function,omitempty"` -} - -type ActuatorSwitchDataElementsType struct { - Function *ElementTagType `json:"function,omitempty"` -} - -type ActuatorSwitchDescriptionDataType struct { - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type ActuatorSwitchDescriptionDataElementsType struct { - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} diff --git a/spine/model/alarm.go b/spine/model/alarm.go deleted file mode 100644 index 7158fce4..00000000 --- a/spine/model/alarm.go +++ /dev/null @@ -1,44 +0,0 @@ -package model - -type AlarmIdType uint - -type AlarmTypeType string - -const ( - AlarmTypeTypeAlarmCancelled AlarmTypeType = "alarmCancelled" - AlarmTypeTypeUnderThreshold AlarmTypeType = "underThreshold" - AlarmTypeTypeOverThreshold AlarmTypeType = "overThreshold" -) - -type AlarmDataType struct { - AlarmId *AlarmIdType `json:"alarmId,omitempty" eebus:"key"` - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty"` - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - AlarmType *AlarmTypeType `json:"alarmType,omitempty"` - MeasuredValue *ScaledNumberType `json:"measuredValue,omitempty"` - EvaluationPeriod *TimePeriodType `json:"evaluationPeriod,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type AlarmDataElementsType struct { - AlarmId *ElementTagType `json:"alarmId,omitempty"` - ThresholdId *ElementTagType `json:"thresholdId,omitempty"` - Timestamp *ElementTagType `json:"timestamp,omitempty"` - AlarmType *ElementTagType `json:"alarmType,omitempty"` - MeasuredValue *ScaledNumberElementsType `json:"measuredValue,omitempty"` - EvaluationPeriod *TimePeriodElementsType `json:"evaluationPeriod,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type AlarmListDataType struct { - AlarmListData []AlarmDataType `json:"alarmListData,omitempty"` -} - -type AlarmListDataSelectorsType struct { - AlarmId *AlarmIdType `json:"alarmId,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} diff --git a/spine/model/alarm_additions.go b/spine/model/alarm_additions.go deleted file mode 100644 index 80e85b72..00000000 --- a/spine/model/alarm_additions.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -// AlarmListDataType - -var _ Updater = (*AlarmListDataType)(nil) - -func (r *AlarmListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []AlarmDataType - if newList != nil { - newData = newList.(*AlarmListDataType).AlarmListData - } - - r.AlarmListData = UpdateList(r.AlarmListData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/alarm_additions_test.go b/spine/model/alarm_additions_test.go deleted file mode 100644 index 10f26ec0..00000000 --- a/spine/model/alarm_additions_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestAlarmListDataType_Update(t *testing.T) { - sut := AlarmListDataType{ - AlarmListData: []AlarmDataType{ - { - AlarmId: util.Ptr(AlarmIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - AlarmId: util.Ptr(AlarmIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := AlarmListDataType{ - AlarmListData: []AlarmDataType{ - { - AlarmId: util.Ptr(AlarmIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.AlarmListData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.AlarmId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.AlarmId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/bill.go b/spine/model/bill.go deleted file mode 100644 index 1d876d41..00000000 --- a/spine/model/bill.go +++ /dev/null @@ -1,157 +0,0 @@ -package model - -type BillIdType uint - -type BillTypeType string - -const ( - BillTypeTypeChargingSummary BillTypeType = "chargingSummary" -) - -type BillPositionIdType uint - -type BillPositionCountType BillPositionIdType - -type BillPositionTypeType string - -const ( - BillPositionTypeTypeGridElectricEnergy BillPositionTypeType = "gridElectricEnergy" - BillPositionTypeTypeSelfProducedElectricEnergy BillPositionTypeType = "selfProducedElectricEnergy" -) - -type BillValueIdType uint - -type BillCostIdType uint - -type BillCostTypeType string - -const ( - BillCostTypeTypeAbsolutePrice BillCostTypeType = "absolutePrice" - BillCostTypeTypeRelativePrice BillCostTypeType = "relativePrice" - BillCostTypeTypeCo2Emission BillCostTypeType = "co2Emission" - BillCostTypeTypeRenewableEnergy BillCostTypeType = "renewableEnergy" - BillCostTypeTypeRadioactiveWaste BillCostTypeType = "radioactiveWaste" -) - -type BillValueType struct { - ValueId *BillValueIdType `json:"valueId,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` - ValuePercentage *ScaledNumberType `json:"valuePercentage,omitempty"` -} - -type BillValueElementsType struct { - ValueId *ElementTagType `json:"valueId,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - Value *ElementTagType `json:"value,omitempty"` - ValuePercentage *ElementTagType `json:"valuePercentage,omitempty"` -} - -type BillCostType struct { - CostId *BillCostIdType `json:"costId,omitempty"` - CostType *BillCostTypeType `json:"costType,omitempty"` - ValueId *BillValueIdType `json:"valueId,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - Currency *CurrencyType `json:"currency,omitempty"` - Cost *ScaledNumberType `json:"cost,omitempty"` - CostPercentage *ScaledNumberType `json:"costPercentage,omitempty"` -} - -type BillCostElementsType struct { - CostId *ElementTagType `json:"costId,omitempty"` - CostType *ElementTagType `json:"costType,omitempty"` - ValueId *ElementTagType `json:"valueId,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - Currency *ElementTagType `json:"currency,omitempty"` - Cost *ScaledNumberElementsType `json:"cost,omitempty"` - CostPercentage *ScaledNumberElementsType `json:"costPercentage,omitempty"` -} - -type BillPositionType struct { - PositionId *BillPositionIdType `json:"positionId,omitempty"` - PositionType *BillPositionTypeType `json:"positionType,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` - Value *BillValueType `json:"value,omitempty"` - Cost *BillCostType `json:"cost,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type BillPositionElementsType struct { - PositionId *ElementTagType `json:"positionId,omitempty"` - PositionType *ElementTagType `json:"positionType,omitempty"` - TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` - Value *BillValueElementsType `json:"value,omitempty"` - Cost *BillCostElementsType `json:"cost,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type BillDataType struct { - BillId *BillIdType `json:"billId,omitempty" eebus:"key"` - BillType *BillTypeType `json:"billType,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Total *BillPositionType `json:"total,omitempty"` - Position []BillPositionType `json:"position,omitempty"` -} - -type BillDataElementsType struct { - BillId *ElementTagType `json:"billId,omitempty"` - BillType *ElementTagType `json:"billType,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Total *BillPositionElementsType `json:"total,omitempty"` - Position *BillPositionElementsType `json:"position,omitempty"` -} - -type BillListDataType struct { - BillData []BillDataType `json:"billData,omitempty"` -} - -type BillListDataSelectorsType struct { - BillId *BillIdType `json:"billId,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} - -type BillConstraintsDataType struct { - BillId *BillIdType `json:"billId,omitempty" eebus:"key"` - PositionCountMin *BillPositionCountType `json:"positionCountMin,omitempty"` - PositionCountMax *BillPositionCountType `json:"positionCountMax,omitempty"` -} - -type BillConstraintsDataElementsType struct { - BillId *ElementTagType `json:"billId,omitempty"` - PositionCountMin *ElementTagType `json:"positionCountMin,omitempty"` - PositionCountMax *ElementTagType `json:"positionCountMax,omitempty"` -} - -type BillConstraintsListDataType struct { - BillConstraintsData []BillConstraintsDataType `json:"billConstraintsData,omitempty"` -} - -type BillConstraintsListDataSelectorsType struct { - BillId *BillIdType `json:"billId,omitempty"` -} - -type BillDescriptionDataType struct { - BillId *BillIdType `json:"billId,omitempty" eebus:"key"` - BillWriteable *bool `json:"billWriteable,omitempty"` - UpdateRequired *bool `json:"updateRequired,omitempty"` - SupportedBillType []BillTypeType `json:"supportedBillType,omitempty"` - SessionId *SessionIdType `json:"sessionId,omitempty"` -} - -type BillDescriptionDataElementsType struct { - BillId *ElementTagType `json:"billId,omitempty"` - BillWriteable *ElementTagType `json:"billWriteable,omitempty"` - UpdateRequired *ElementTagType `json:"updateRequired,omitempty"` - SupportedBillType *ElementTagType `json:"supportedBillType,omitempty"` - SessionId *ElementTagType `json:"sessionId,omitempty"` -} - -type BillDescriptionListDataType struct { - BillDescriptionData []BillDescriptionDataType `json:"billDescriptionData,omitempty"` -} - -type BillDescriptionListDataSelectorsType struct { - BillId *BillIdType `json:"billId,omitempty"` -} diff --git a/spine/model/bill_additions.go b/spine/model/bill_additions.go deleted file mode 100644 index 8e08654e..00000000 --- a/spine/model/bill_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// BillListDataType - -var _ Updater = (*BillListDataType)(nil) - -func (r *BillListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []BillDataType - if newList != nil { - newData = newList.(*BillListDataType).BillData - } - - r.BillData = UpdateList(r.BillData, newData, filterPartial, filterDelete) -} - -// BillConstraintsListDataType - -var _ Updater = (*BillConstraintsListDataType)(nil) - -func (r *BillConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []BillConstraintsDataType - if newList != nil { - newData = newList.(*BillConstraintsListDataType).BillConstraintsData - } - - r.BillConstraintsData = UpdateList(r.BillConstraintsData, newData, filterPartial, filterDelete) -} - -// BillDescriptionListDataType - -var _ Updater = (*BillDescriptionListDataType)(nil) - -func (r *BillDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []BillDescriptionDataType - if newList != nil { - newData = newList.(*BillDescriptionListDataType).BillDescriptionData - } - - r.BillDescriptionData = UpdateList(r.BillDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/bill_additions_test.go b/spine/model/bill_additions_test.go deleted file mode 100644 index 9d29809e..00000000 --- a/spine/model/bill_additions_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestBillListDataType_Update(t *testing.T) { - sut := BillListDataType{ - BillData: []BillDataType{ - { - BillId: util.Ptr(BillIdType(0)), - ScopeType: util.Ptr(ScopeTypeTypeACCurrent), - }, - { - BillId: util.Ptr(BillIdType(1)), - ScopeType: util.Ptr(ScopeTypeTypeACCurrent), - }, - }, - } - - newData := BillListDataType{ - BillData: []BillDataType{ - { - BillId: util.Ptr(BillIdType(1)), - ScopeType: util.Ptr(ScopeTypeTypeACPower), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.BillData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.BillId)) - assert.Equal(t, ScopeTypeTypeACCurrent, *item1.ScopeType) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.BillId)) - assert.Equal(t, ScopeTypeTypeACPower, *item2.ScopeType) -} - -func TestBillConstraintsListDataType_Update(t *testing.T) { - sut := BillConstraintsListDataType{ - BillConstraintsData: []BillConstraintsDataType{ - { - BillId: util.Ptr(BillIdType(0)), - PositionCountMin: util.Ptr(BillPositionCountType(0)), - }, - { - BillId: util.Ptr(BillIdType(1)), - PositionCountMin: util.Ptr(BillPositionCountType(0)), - }, - }, - } - - newData := BillConstraintsListDataType{ - BillConstraintsData: []BillConstraintsDataType{ - { - BillId: util.Ptr(BillIdType(1)), - PositionCountMin: util.Ptr(BillPositionCountType(1)), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.BillConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.BillId)) - assert.Equal(t, 0, int(*item1.PositionCountMin)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.BillId)) - assert.Equal(t, 1, int(*item2.PositionCountMin)) -} - -func TestBillDescriptionListDataType_Update(t *testing.T) { - sut := BillDescriptionListDataType{ - BillDescriptionData: []BillDescriptionDataType{ - { - BillId: util.Ptr(BillIdType(0)), - UpdateRequired: util.Ptr(false), - }, - { - BillId: util.Ptr(BillIdType(1)), - UpdateRequired: util.Ptr(false), - }, - }, - } - - newData := BillDescriptionListDataType{ - BillDescriptionData: []BillDescriptionDataType{ - { - BillId: util.Ptr(BillIdType(1)), - UpdateRequired: util.Ptr(true), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.BillDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.BillId)) - assert.Equal(t, false, *item1.UpdateRequired) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.BillId)) - assert.Equal(t, true, *item2.UpdateRequired) -} diff --git a/spine/model/bindingmanagement.go b/spine/model/bindingmanagement.go deleted file mode 100644 index 719d6716..00000000 --- a/spine/model/bindingmanagement.go +++ /dev/null @@ -1,53 +0,0 @@ -package model - -type BindingIdType uint - -type BindingManagementEntryDataType struct { - BindingId *BindingIdType `json:"bindingId,omitempty" eebus:"key"` - ClientAddress *FeatureAddressType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressType `json:"serverAddress,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type BindingManagementEntryDataElementsType struct { - BindingId *ElementTagType `json:"bindingId,omitempty"` - ClientAddress *ElementTagType `json:"clientAddress,omitempty"` - ServerAddress *ElementTagType `json:"serverAddress,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type BindingManagementEntryListDataType struct { - BindingManagementEntryData []BindingManagementEntryDataType `json:"bindingManagementEntryData,omitempty"` -} - -type BindingManagementEntryListDataSelectorsType struct { - BindingId *BindingIdType `json:"bindingId,omitempty"` - ClientAddress *FeatureAddressType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressType `json:"serverAddress,omitempty"` -} - -type BindingManagementRequestCallType struct { - ClientAddress *FeatureAddressType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressType `json:"serverAddress,omitempty"` - ServerFeatureType *FeatureTypeType `json:"serverFeatureType,omitempty"` -} - -type BindingManagementRequestCallElementsType struct { - ClientAddress *ElementTagType `json:"clientAddress,omitempty"` - ServerAddress *ElementTagType `json:"serverAddress,omitempty"` - ServerFeatureType *ElementTagType `json:"serverFeatureType,omitempty"` -} - -type BindingManagementDeleteCallType struct { - BindingId *BindingIdType `json:"bindingId,omitempty"` - ClientAddress *FeatureAddressType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressType `json:"serverAddress,omitempty"` -} - -type BindingManagementDeleteCallElementsType struct { - BindingId *ElementTagType `json:"bindingId,omitempty"` - ClientAddress *ElementTagType `json:"clientAddress,omitempty"` - ServerAddress *ElementTagType `json:"serverAddress,omitempty"` -} diff --git a/spine/model/bindingmanagement_additions.go b/spine/model/bindingmanagement_additions.go deleted file mode 100644 index c7d31e97..00000000 --- a/spine/model/bindingmanagement_additions.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -// BindingManagementEntryListDataType - -var _ Updater = (*BindingManagementEntryListDataType)(nil) - -func (r *BindingManagementEntryListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []BindingManagementEntryDataType - if newList != nil { - newData = newList.(*BindingManagementEntryListDataType).BindingManagementEntryData - } - - r.BindingManagementEntryData = UpdateList(r.BindingManagementEntryData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/bindingmanagement_additions_test.go b/spine/model/bindingmanagement_additions_test.go deleted file mode 100644 index 009db2b4..00000000 --- a/spine/model/bindingmanagement_additions_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestBindingManagementEntryListDataType_Update(t *testing.T) { - sut := BindingManagementEntryListDataType{ - BindingManagementEntryData: []BindingManagementEntryDataType{ - { - BindingId: util.Ptr(BindingIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - BindingId: util.Ptr(BindingIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := BindingManagementEntryListDataType{ - BindingManagementEntryData: []BindingManagementEntryDataType{ - { - BindingId: util.Ptr(BindingIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.BindingManagementEntryData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.BindingId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.BindingId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/collection_operations.go b/spine/model/collection_operations.go deleted file mode 100644 index 84d6ccc6..00000000 --- a/spine/model/collection_operations.go +++ /dev/null @@ -1,122 +0,0 @@ -package model - -import ( - "fmt" - "reflect" -) - -// creates an hash key by using fields that have eebus tag "key" -func hashKey(data any) string { - result := "" - - keys := keyFieldNames(data) - - if len(keys) == 0 { - return result - } - - v := reflect.ValueOf(data) - - for _, fieldName := range keys { - f := v.FieldByName(fieldName) - - if f.IsNil() || !f.IsValid() { - return result - } - - switch f.Elem().Kind() { - case reflect.String: - value := f.Elem().String() - - if len(result) > 0 { - result = fmt.Sprintf("%s|", result) - } - result = fmt.Sprintf("%s%s", result, value) - - case reflect.Uint: - value := f.Elem().Uint() - - if len(result) > 0 { - result = fmt.Sprintf("%s|", result) - } - result = fmt.Sprintf("%s%d", result, value) - - default: - return result - } - } - - return result -} - -// Merges two slices into one. The item in the first slice will be replaced by the one in the second slice -// if the hash key is the same. Items in the second slice which are not in the first will be added. -func Merge[T any](s1 []T, s2 []T) []T { - result := []T{} - - m2 := ToMap(s2) - - // go through the first slice - m1 := make(map[string]T, len(s1)) - for _, s1Item := range s1 { - s1ItemHash := hashKey(s1Item) - // s1ItemHash := s1Item.HashKey() - s2Item, exist := m2[s1ItemHash] - if exist { - // the item in the first slice will be replaces by the one of the second slice - result = append(result, s2Item) - } else { - result = append(result, s1Item) - } - - m1[s1ItemHash] = s1Item - } - - // append items which were not in the first slice - for _, s2Item := range s2 { - s2ItemHash := hashKey(s2Item) - // s2ItemHash := s2Item.HashKey() - _, exist := m1[s2ItemHash] - if !exist { - result = append(result, s2Item) - } - } - - return result -} - -func ToMap[T any](s []T) map[string]T { - result := make(map[string]T, len(s)) - for _, item := range s { - result[hashKey(item)] = item - } - return result -} - -/* -func FindFirst[T any](s []T, predicate func(i T) bool) *T { - for _, item := range s { - if predicate(item) { - return &item - } - } - return nil -} - -func Values[K comparable, V any](m map[K]V) []V { - ret := make([]V, 0, len(m)) - for _, v := range m { - ret = append(ret, v) - } - return ret -} - -// casts all elements in slice s to type D -func CastElements[S any, D any](s []S) []D { - result := make([]D, len(s)) - for i, item := range s { - result[i] = any(item).(D) - } - return result -} -*/ diff --git a/spine/model/collection_operations_test.go b/spine/model/collection_operations_test.go deleted file mode 100644 index c22f67ec..00000000 --- a/spine/model/collection_operations_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package model - -import ( - "fmt" - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -type testStruct struct { - id *uint `eebus:"key"` - data *string -} - -func (r testStruct) HashKey() string { - return fmt.Sprintf("%d", r.id) -} - -func TestUnion_NewData(t *testing.T) { - existingData := []testStruct{ - {id: util.Ptr(uint(1)), data: util.Ptr(string("data1"))}, - } - - newData := []testStruct{ - {id: util.Ptr(uint(2)), data: util.Ptr(string("data2"))}, - } - - // Act - result := Merge(existingData, newData) - - if assert.Equal(t, 2, len(result)) { - assert.Equal(t, 1, int(*result[0].id)) - assert.Equal(t, "data1", string(*result[0].data)) - assert.Equal(t, 2, int(*result[1].id)) - assert.Equal(t, "data2", string(*result[1].data)) - } -} - -func TestUnion_NewAndUpdateData(t *testing.T) { - existingData := []testStruct{ - {id: util.Ptr(uint(1)), data: util.Ptr(string("data1"))}, - {id: util.Ptr(uint(2)), data: util.Ptr(string("data2"))}, - } - - newData := []testStruct{ - {id: util.Ptr(uint(2)), data: util.Ptr(string("data22"))}, - {id: util.Ptr(uint(3)), data: util.Ptr(string("data33"))}, - } - - // Act - result := Merge(existingData, newData) - - if assert.Equal(t, 3, len(result)) { - assert.Equal(t, 1, int(*result[0].id)) - assert.Equal(t, "data1", string(*result[0].data)) - assert.Equal(t, 2, int(*result[1].id)) - assert.Equal(t, "data22", string(*result[1].data)) - assert.Equal(t, 3, int(*result[2].id)) - assert.Equal(t, "data33", string(*result[2].data)) - } -} diff --git a/spine/model/commandframe.go b/spine/model/commandframe.go deleted file mode 100644 index 537e2075..00000000 --- a/spine/model/commandframe.go +++ /dev/null @@ -1,426 +0,0 @@ -package model - -type MsgCounterType uint64 - -type CmdClassifierType string - -const ( - CmdClassifierTypeRead CmdClassifierType = "read" - CmdClassifierTypeReply CmdClassifierType = "reply" - CmdClassifierTypeNotify CmdClassifierType = "notify" - CmdClassifierTypeWrite CmdClassifierType = "write" - CmdClassifierTypeCall CmdClassifierType = "call" - CmdClassifierTypeResult CmdClassifierType = "result" -) - -type FilterIdType uint - -type FilterType struct { - FilterId *FilterIdType `json:"filterId,omitempty"` - CmdControl *CmdControlType `json:"cmdControl,omitempty"` - - // DataSelectorsChoiceGroup - AlarmListDataSelectors *AlarmListDataSelectorsType `json:"alarmListDataSelectors,omitempty" eebus:"typ:selector,fct:alarmListData"` - BillConstraintsListDataSelectors *BillConstraintsListDataSelectorsType `json:"billConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:billConstraintsListData"` - BillDescriptionListDataSelectors *BillDescriptionListDataSelectorsType `json:"billDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:billDescriptionListData"` - BillListDataSelectors *BillListDataSelectorsType `json:"billListDataSelectors,omitempty" eebus:"typ:selector,fct:billListData"` - BindingManagementEntryListDataSelectors *BindingManagementEntryListDataSelectorsType `json:"bindingManagementEntryListDataSelectors,omitempty" eebus:"typ:selector,fct:bindingManagementEntryListData"` - CommodityListDataSelectors *CommodityListDataSelectorsType `json:"commodityListDataSelectors,omitempty" eebus:"typ:selector,fct:commodityListData"` - DeviceConfigurationKeyValueConstraintsListDataSelectors *DeviceConfigurationKeyValueConstraintsListDataSelectorsType `json:"deviceConfigurationKeyValueConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:deviceConfigurationKeyValueConstraintsListData"` - DeviceConfigurationKeyValueDescriptionListDataSelectors *DeviceConfigurationKeyValueDescriptionListDataSelectorsType `json:"deviceConfigurationKeyValueDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:deviceConfigurationKeyValueDescriptionListData"` - DeviceConfigurationKeyValueListDataSelectors *DeviceConfigurationKeyValueListDataSelectorsType `json:"deviceConfigurationKeyValueListDataSelectors,omitempty" eebus:"typ:selector,fct:deviceConfigurationKeyValueListData"` - DirectControlActivityListDataSelectors *DirectControlActivityListDataSelectorsType `json:"directControlActivityListDataSelectors,omitempty" eebus:"typ:selector,fct:directControlActivityListData"` - ElectricalConnectionDescriptionListDataSelectors *ElectricalConnectionDescriptionListDataSelectorsType `json:"electricalConnectionDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionDescriptionListData"` - ElectricalConnectionParameterDescriptionListDataSelectors *ElectricalConnectionParameterDescriptionListDataSelectorsType `json:"electricalConnectionParameterDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionParameterDescriptionListData"` - ElectricalConnectionPermittedValueSetListDataSelectors *ElectricalConnectionPermittedValueSetListDataSelectorsType `json:"electricalConnectionPermittedValueSetListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionPermittedValueSetListData"` - ElectricalConnectionStateListDataSelectors *ElectricalConnectionStateListDataSelectorsType `json:"electricalConnectionStateListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionStateListData"` - ElectricalConnectionCharacteristicListDataSelectors *ElectricalConnectionCharacteristicListDataSelectorsType `json:"electricalConnectionCharacteristicListDataSelectors,omitempty" eebus:"typ:selector,fct:electricalConnectionCharacteristicListData"` - HvacOperationModeDescriptionListDataSelectors *HvacOperationModeDescriptionListDataSelectorsType `json:"hvacOperationModeDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacOperationModeDescriptionListData"` - HvacOverrunDescriptionListDataSelectors *HvacOverrunDescriptionListDataSelectorsType `json:"hvacOverrunDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacOverrunDescriptionListData"` - HvacOverrunListDataSelectors *HvacOverrunListDataSelectorsType `json:"hvacOverrunListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacOverrunListData"` - HvacSystemFunctionDescriptionListDataSelectors *HvacSystemFunctionDescriptionListDataSelectorsType `json:"hvacSystemFunctionDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacSystemFunctionDescriptionListData"` - HvacSystemFunctionListDataSelectors *HvacSystemFunctionListDataSelectorsType `json:"hvacSystemFunctionListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacSystemFunctionListData"` - HvacSystemFunctionOperationModeRelationListDataSelectors *HvacSystemFunctionOperationModeRelationListDataSelectorsType `json:"hvacSystemFunctionOperationModeRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacSystemFunctionOperationModeRelationListData"` - HvacSystemFunctionPowerSequenceRelationListDataSelectors *HvacSystemFunctionPowerSequenceRelationListDataSelectorsType `json:"hvacSystemFunctionPowerSequenceRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacSystemFunctionPowerSequenceRelationListData"` - HvacSystemFunctionSetpointRelationListDataSelectors *HvacSystemFunctionSetpointRelationListDataSelectorsType `json:"hvacSystemFunctionSetpointRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:hvacSystemFunctionSetpointRelationListData"` - IdentificationListDataSelectors *IdentificationListDataSelectorsType `json:"identificationListDataSelectors,omitempty" eebus:"typ:selector,fct:identificationListData"` - IncentiveDescriptionListDataSelectors *IncentiveDescriptionListDataSelectorsType `json:"incentiveDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:incentiveDescriptionListData"` - IncentiveListDataSelectors *IncentiveListDataSelectorsType `json:"incentiveListDataSelectors,omitempty" eebus:"typ:selector,fct:incentiveListData"` - IncentiveTableConstraintsDataSelectors *IncentiveTableConstraintsDataSelectorsType `json:"incentiveTableConstraintsDataSelectors,omitempty" eebus:"typ:selector,fct:incentiveTableConstraintsData"` - IncentiveTableDataSelectors *IncentiveTableDataSelectorsType `json:"incentiveTableDataSelectors,omitempty" eebus:"typ:selector,fct:incentiveTableData"` - IncentiveTableDescriptionDataSelectors *IncentiveTableDescriptionDataSelectorsType `json:"incentiveTableDescriptionDataSelectors,omitempty" eebus:"typ:selector,fct:incentiveTableDescriptionData"` - LoadControlEventListDataSelectors *LoadControlEventListDataSelectorsType `json:"loadControlEventListDataSelectors,omitempty" eebus:"typ:selector,fct:loadControlEventListData"` - LoadControlLimitConstraintsListDataSelectors *LoadControlLimitConstraintsListDataSelectorsType `json:"loadControlLimitConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:loadControlLimitConstraintsListData"` - LoadControlLimitDescriptionListDataSelectors *LoadControlLimitDescriptionListDataSelectorsType `json:"loadControlLimitDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:loadControlLimitDescriptionListData"` - LoadControlLimitListDataSelectors *LoadControlLimitListDataSelectorsType `json:"loadControlLimitListDataSelectors,omitempty" eebus:"typ:selector,fct:loadControlLimitListData"` - LoadControlStateListDataSelectors *LoadControlStateListDataSelectorsType `json:"loadControlStateListDataSelectors,omitempty" eebus:"typ:selector,fct:loadControlStateListData"` - MeasurementConstraintsListDataSelectors *MeasurementConstraintsListDataSelectorsType `json:"measurementConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:measurementConstraintsListData"` - MeasurementDescriptionListDataSelectors *MeasurementDescriptionListDataSelectorsType `json:"measurementDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:measurementDescriptionListData"` - MeasurementListDataSelectors *MeasurementListDataSelectorsType `json:"measurementListDataSelectors,omitempty" eebus:"typ:selector,fct:measurementListData"` - MeasurementSeriesListDataSelectors *MeasurementSeriesListDataSelectorsType `json:"measurementSeriesListDataSelectors,omitempty" eebus:"measurementSeriesListData"` - MeasurementThresholdRelationListDataSelectors *MeasurementThresholdRelationListDataSelectorsType `json:"measurementThresholdRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:measurementThresholdRelationListData"` - MessagingListDataSelectors *MessagingListDataSelectorsType `json:"messagingListDataSelectors,omitempty" eebus:"typ:selector,fct:messagingListData"` - NetworkManagementDeviceDescriptionListDataSelectors *NetworkManagementDeviceDescriptionListDataSelectorsType `json:"networkManagementDeviceDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:networkManagementDeviceDescriptionListData"` - NetworkManagementEntityDescriptionListDataSelectors *NetworkManagementEntityDescriptionListDataSelectorsType `json:"networkManagementEntityDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:networkManagementEntityDescriptionListData"` - NetworkManagementFeatureDescriptionListDataSelectors *NetworkManagementFeatureDescriptionListDataSelectorsType `json:"networkManagementFeatureDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:networkManagementFeatureDescriptionList"` - NodeManagementBindingDataSelectors *NodeManagementBindingDataSelectorsType `json:"nodeManagementBindingDataSelectors,omitempty" eebus:"typ:selector,fct:nodeManagementBindingData"` - NodeManagementDestinationListDataSelectors *NodeManagementDestinationListDataSelectorsType `json:"nodeManagementDestinationListDataSelectors,omitempty" eebus:"typ:selector,fct:nodeManagementDestinationListData"` - NodeManagementDetailedDiscoveryDataSelectors *NodeManagementDetailedDiscoveryDataSelectorsType `json:"nodeManagementDetailedDiscoveryDataSelectors,omitempty" eebus:"typ:selector,fct:nodeManagementDetailedDiscoveryData"` - NodeManagementSubscriptionDataSelectors *NodeManagementSubscriptionDataSelectorsType `json:"nodeManagementSubscriptionDataSelectors,omitempty" eebus:"typ:selector,fct:nodeManagementSubscriptionData"` - NodeManagementUseCaseDataSelectors *NodeManagementUseCaseDataSelectorsType `json:"nodeManagementUseCaseDataSelectors,omitempty" eebus:"typ:selector,fct:nodeManagementUseCaseData"` - OperatingConstraintsDurationListDataSelectors *OperatingConstraintsDurationListDataSelectorsType `json:"operatingConstraintsDurationListDataSelectors,omitempty" eebus:"typ:selector,fct:operatingConstraintsDurationListData"` - OperatingConstraintsInterruptListDataSelectors *OperatingConstraintsInterruptListDataSelectorsType `json:"operatingConstraintsInterruptListDataSelectors,omitempty" eebus:"typ:selector,fct:operatingConstraintsInterruptListData"` - OperatingConstraintsPowerDescriptionListDataSelectors *OperatingConstraintsPowerDescriptionListDataSelectorsType `json:"operatingConstraintsPowerDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:operatingConstraintsPowerDescriptionListData"` - OperatingConstraintsPowerLevelListDataSelectors *OperatingConstraintsPowerLevelListDataSelectorsType `json:"operatingConstraintsPowerLevelListDataSelectors,omitempty" eebus:"typ:selector,fct:operatingConstraintsPowerLevelListData"` - OperatingConstraintsPowerRangeListDataSelectors *OperatingConstraintsPowerRangeListDataSelectorsType `json:"operatingConstraintsPowerRangeListDataSelectors,omitempty" eebus:"typ:selector,fct:operatingConstraintsPowerRangeListData"` - OperatingConstraintsResumeImplicationListDataSelectors *OperatingConstraintsResumeImplicationListDataSelectorsType `json:"operatingConstraintsResumeImplicationListDataSelectors,omitempty" eebus:"typ:selector,fct:operatingConstraintsResumeImplicationListData"` - PowerSequenceAlternativesRelationListDataSelectors *PowerSequenceAlternativesRelationListDataSelectorsType `json:"powerSequenceAlternativesRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:powerSequenceAlternativesRelationListData"` - PowerSequenceDescriptionListDataSelectors *PowerSequenceDescriptionListDataSelectorsType `json:"powerSequenceDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:powerSequenceDescriptionListData"` - PowerSequencePriceListDataSelectors *PowerSequencePriceListDataSelectorsType `json:"powerSequencePriceListDataSelectors,omitempty" eebus:"typ:selector,fct:powerSequencePriceListData"` - PowerSequenceScheduleConstraintsListDataSelectors *PowerSequenceScheduleConstraintsListDataSelectorsType `json:"powerSequenceScheduleConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:powerSequenceScheduleConstraintsListData"` - PowerSequenceScheduleListDataSelectors *PowerSequenceScheduleListDataSelectorsType `json:"powerSequenceScheduleListDataSelectors,omitempty" eebus:"typ:selector,fct:powerSequenceScheduleListData"` - PowerSequenceSchedulePreferenceListDataSelectors *PowerSequenceSchedulePreferenceListDataSelectorsType `json:"powerSequenceSchedulePreferenceListDataSelectors,omitempty" eebus:"typ:selector,fct:powerSequenceSchedulePreferenceListData"` - PowerSequenceStateListDataSelectors *PowerSequenceStateListDataSelectorsType `json:"powerSequenceStateListDataSelectors,omitempty" eebus:"typ:selector,fct:powerSequenceStateListData"` - PowerTimeSlotScheduleConstraintsListDataSelectors *PowerTimeSlotScheduleConstraintsListDataSelectorsType `json:"powerTimeSlotScheduleConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:powerTimeSlotScheduleConstraintsListData"` - PowerTimeSlotScheduleListDataSelectors *PowerTimeSlotScheduleListDataSelectorsType `json:"powerTimeSlotScheduleListDataSelectors,omitempty" eebus:"typ:selector,fct:powerTimeSlotScheduleListData"` - PowerTimeSlotValueListDataSelectors *PowerTimeSlotValueListDataSelectorsType `json:"powerTimeSlotValueListDataSelectors,omitempty" eebus:"typ:selector,fct:powerTimeSlotValueListData"` - SensingListDataSelectors *SensingListDataSelectorsType `json:"sensingListDataSelectors,omitempty" eebus:"typ:selector,fct:sensingListData"` - SessionIdentificationListDataSelectors *SessionIdentificationListDataSelectorsType `json:"sessionIdentificationListDataSelectors,omitempty" eebus:"typ:selector,fct:sessionIdentificationListData"` - SessionMeasurementRelationListDataSelectors *SessionMeasurementRelationListDataSelectorsType `json:"sessionMeasurementRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:sessionMeasurementRelationListData"` - SetpointConstraintsListDataSelectors *SetpointConstraintsListDataSelectorsType `json:"setpointConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:setpointConstraintsListData"` - SetpointDescriptionListDataSelectors *SetpointDescriptionListDataSelectorsType `json:"setpointDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:setpointDescriptionListData"` - SetpointListDataSelectors *SetpointListDataSelectorsType `json:"setpointListDataSelectors,omitempty" eebus:"typ:selector,fct:setpointListData"` - SmartEnergyManagementPsDataSelectors *SmartEnergyManagementPsDataSelectorsType `json:"smartEnergyManagementPsDataSelectors,omitempty" eebus:"typ:selector,fct:smartEnergyManagementPsData"` - SmartEnergyManagementPsPriceDataSelectors *SmartEnergyManagementPsPriceDataSelectorsType `json:"smartEnergyManagementPsPriceDataSelectors,omitempty" eebus:"typ:selector,fct:smartEnergyManagementPsPriceData"` - SpecificationVersionListDataSelectors *SpecificationVersionListDataSelectorsType `json:"specificationVersionListDataSelectors,omitempty" eebus:"typ:selector,fct:specificationVersionListData"` - StateInformationListDataSelectors *StateInformationListDataSelectorsType `json:"stateInformationListDataSelectors,omitempty" eebus:"typ:selector,fct:stateInformationListData"` - SubscriptionManagementEntryListDataSelectors *SubscriptionManagementEntryListDataSelectorsType `json:"subscriptionManagementEntryListDataSelectors,omitempty" eebus:"typ:selector,fct:subscriptionManagementEntryListData"` - SupplyConditionDescriptionListDataSelectors *SupplyConditionDescriptionListDataSelectorsType `json:"supplyConditionDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:supplyConditionDescriptionListData"` - SupplyConditionListDataSelectors *SupplyConditionListDataSelectorsType `json:"supplyConditionListDataSelectors,omitempty" eebus:"typ:selector,fct:supplyConditionListData"` - SupplyConditionThresholdRelationListDataSelectors *SupplyConditionThresholdRelationListDataSelectorsType `json:"supplyConditionThresholdRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:supplyConditionThresholdRelationListData"` - TariffBoundaryRelationListDataSelectors *TariffBoundaryRelationListDataSelectorsType `json:"tariffBoundaryRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:tariffBoundaryRelationListData"` - TariffDescriptionListDataSelectors *TariffDescriptionListDataSelectorsType `json:"tariffDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:tariffDescriptionListData"` - TariffListDataSelectors *TariffListDataSelectorsType `json:"tariffListDataSelectors,omitempty" eebus:"typ:selector,fct:tariffListData"` - TariffTierRelationListDataSelectors *TariffTierRelationListDataSelectorsType `json:"tariffTierRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:tariffTierRelationListData"` - TaskManagementJobDescriptionListDataSelectors *TaskManagementJobDescriptionListDataSelectorsType `json:"taskManagementJobDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:taskManagementJobDescriptionListData"` - TaskManagementJobListDataSelectors *TaskManagementJobListDataSelectorsType `json:"taskManagementJobListDataSelectors,omitempty" eebus:"typ:selector,fct:taskManagementJobListData"` - TaskManagementJobRelationListDataSelectors *TaskManagementJobRelationListDataSelectorsType `json:"taskManagementJobRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:taskManagementJobRelationListData"` - ThresholdConstraintsListDataSelectors *ThresholdConstraintsListDataSelectorsType `json:"thresholdConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:thresholdConstraintsListData"` - ThresholdDescriptionListDataSelectors *ThresholdDescriptionListDataSelectorsType `json:"thresholdDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:thresholdDescriptionListData"` - ThresholdListDataSelectors *ThresholdListDataSelectorsType `json:"thresholdListDataSelectors,omitempty" eebus:"typ:selector,fct:thresholdListData"` - TierBoundaryDescriptionListDataSelectors *TierBoundaryDescriptionListDataSelectorsType `json:"tierBoundaryDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:tierBoundaryDescriptionListData"` - TierBoundaryListDataSelectors *TierBoundaryListDataSelectorsType `json:"tierBoundaryListDataSelectors,omitempty" eebus:"typ:selector,fct:tierBoundaryListData"` - TierDescriptionListDataSelectors *TierDescriptionListDataSelectorsType `json:"tierDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:tierDescriptionListData"` - TierIncentiveRelationListDataSelectors *TierIncentiveRelationListDataSelectorsType `json:"tierIncentiveRelationListDataSelectors,omitempty" eebus:"typ:selector,fct:tierIncentiveRelationListData"` - TierListDataSelectors *TierListDataSelectorsType `json:"tierListDataSelectors,omitempty" eebus:"typ:selector,fct:tierListData"` - TimeSeriesConstraintsListDataSelectors *TimeSeriesConstraintsListDataSelectorsType `json:"timeSeriesConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:timeSeriesConstraintsListData"` - TimeSeriesDescriptionListDataSelectors *TimeSeriesDescriptionListDataSelectorsType `json:"timeSeriesDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:timeSeriesDescriptionListData"` - TimeSeriesListDataSelectors *TimeSeriesListDataSelectorsType `json:"timeSeriesListDataSelectors,omitempty" eebus:"typ:selector,fct:timeSeriesListData"` - TimeTableConstraintsListDataSelectors *TimeTableConstraintsListDataSelectorsType `json:"timeTableConstraintsListDataSelectors,omitempty" eebus:"typ:selector,fct:timeTableConstraintsListData"` - TimeTableDescriptionListDataSelectors *TimeTableDescriptionListDataSelectorsType `json:"timeTableDescriptionListDataSelectors,omitempty" eebus:"typ:selector,fct:timeTableDescriptionListData"` - TimeTableListDataSelectors *TimeTableListDataSelectorsType `json:"timeTableListDataSelectors,omitempty" eebus:"typ:selector,fct:timeTableListData"` - UseCaseInformationListDataSelectors *UseCaseInformationListDataSelectorsType `json:"useCaseInformationListDataSelectors,omitempty" eebus:"typ:selector,fct:useCaseInformationListData"` - - // DataElementsChoiceGroup - ActuatorLevelDataElements *ActuatorLevelDataElementsType `json:"actuatorLevelDataElements,omitempty" eebus:"typ:elements,fct:actuatorLevelData"` - ActuatorLevelDescriptionDataElements *ActuatorLevelDescriptionDataElementsType `json:"actuatorLevelDescriptionDataElements,omitempty" eebus:"typ:elements,fct:actuatorLevelDescriptionData"` - ActuatorSwitchDataElements *ActuatorSwitchDataElementsType `json:"actuatorSwitchDataElements,omitempty" eebus:"typ:elements,fct:actuatorSwitchData"` - ActuatorSwitchDescriptionDataElements *ActuatorSwitchDescriptionDataElementsType `json:"actuatorSwitchDescriptionDataElements,omitempty" eebus:"typ:elements,fct:actuatorSwitchDescriptionData"` - AlarmDataElements *AlarmDataElementsType `json:"alarmDataElements,omitempty" eebus:"typ:elements,fct:alarmListData"` - BillConstraintsDataElements *BillConstraintsDataElementsType `json:"billConstraintsDataElements,omitempty" eebus:"typ:elements,fct:billConstraintsListData"` - BillDataElements *BillDataElementsType `json:"billDataElements,omitempty" eebus:"typ:elements,fct:billListData"` - BillDescriptionDataElements *BillDescriptionDataElementsType `json:"billDescriptionDataElements,omitempty" eebus:"typ:elements,fct:billDescriptionListData"` - BindingManagementDeleteCallElements *BindingManagementDeleteCallElementsType `json:"bindingManagementDeleteCallElements,omitempty" eebus:"typ:elements,fct:bindingManagementDeleteCall"` - BindingManagementEntryDataElements *BindingManagementEntryDataElementsType `json:"bindingManagementEntryDataElements,omitempty" eebus:"typ:elements,fct:bindingManagementEntryListData"` - BindingManagementRequestCallElements *BindingManagementRequestCallElementsType `json:"bindingManagementRequestCallElements,omitempty" eebus:"typ:elements,fct:bindingManagementRequestCall"` - CommodityDataElements *CommodityDataElementsType `json:"commodityDataElements,omitempty" eebus:"typ:elements,fct:commodityListData"` - DataTunnelingCallElements *DataTunnelingCallElementsType `json:"dataTunnelingCallElements,omitempty" eebus:"typ:elements,fct:dataTunnelingCall"` - DeviceClassificationManufacturerDataElements *DeviceClassificationManufacturerDataElementsType `json:"deviceClassificationManufacturerDataElements,omitempty" eebus:"typ:elements,fct:deviceClassificationManufacturerData"` - DeviceClassificationUserDataElements *DeviceClassificationUserDataElementsType `json:"deviceClassificationUserDataElements,omitempty" eebus:"typ:elements,fct:deviceClassificationUserData"` - DeviceConfigurationKeyValueConstraintsDataElements *DeviceConfigurationKeyValueConstraintsDataElementsType `json:"deviceConfigurationKeyValueConstraintsDataElements,omitempty" eebus:"typ:elements,fct:deviceConfigurationKeyValueConstraintsListData"` - DeviceConfigurationKeyValueDataElements *DeviceConfigurationKeyValueDataElementsType `json:"deviceConfigurationKeyValueDataElements,omitempty" eebus:"typ:elements,fct:deviceConfigurationKeyValueListData"` - DeviceConfigurationKeyValueDescriptionDataElements *DeviceConfigurationKeyValueDescriptionDataElementsType `json:"deviceConfigurationKeyValueDescriptionDataElements,omitempty" eebus:"typ:elements,fct:deviceConfigurationKeyValueDescriptionListData"` - DeviceDiagnosisHeartbeatDataElements *DeviceDiagnosisHeartbeatDataElementsType `json:"deviceDiagnosisHeartbeatDataElements,omitempty" eebus:"typ:elements,fct:deviceDiagnosisHeartbeatData"` - DeviceDiagnosisServiceDataElements *DeviceDiagnosisServiceDataElementsType `json:"deviceDiagnosisServiceDataElements,omitempty" eebus:"typ:elements,fct:deviceDiagnosisServiceData"` - DeviceDiagnosisStateDataElements *DeviceDiagnosisStateDataElementsType `json:"deviceDiagnosisStateDataElements,omitempty" eebus:"typ:elements,fct:deviceDiagnosisStateData"` - DirectControlActivityDataElements *DirectControlActivityDataElementsType `json:"directControlActivityDataElements,omitempty" eebus:"typ:elements,fct:directControlActivityListData"` - DirectControlDescriptionDataElements *DirectControlDescriptionDataElementsType `json:"directControlDescriptionDataElements,omitempty" eebus:"typ:elements,fct:directControlDescriptionData"` - ElectricalConnectionDescriptionDataElements *ElectricalConnectionDescriptionDataElementsType `json:"electricalConnectionDescriptionDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionDescriptionListData"` - ElectricalConnectionParameterDescriptionDataElements *ElectricalConnectionParameterDescriptionDataElementsType `json:"electricalConnectionParameterDescriptionDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionParameterDescriptionListData"` - ElectricalConnectionPermittedValueSetDataElements *ElectricalConnectionPermittedValueSetDataElementsType `json:"electricalConnectionPermittedValueSetDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionPermittedValueSetListData"` - ElectricalConnectionStateDataElements *ElectricalConnectionStateDataElementsType `json:"electricalConnectionStateDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionStateListData"` - ElectricalConnectionCharacteristicDataElements *ElectricalConnectionCharacteristicDataElementsType `json:"electricalConnectionCharacteristicDataElements,omitempty" eebus:"typ:elements,fct:electricalConnectionCharacteristicListData"` - HvacOperationModeDescriptionDataElements *HvacOperationModeDescriptionDataElementsType `json:"hvacOperationModeDescriptionDataElements,omitempty" eebus:"typ:elements,fct:hvacOperationModeDescriptionListData"` - HvacOverrunDataElements *HvacOverrunDataElementsType `json:"hvacOverrunDataElements,omitempty" eebus:"typ:elements,fct:hvacOverrunListData"` - HvacOverrunDescriptionDataElements *HvacOverrunDescriptionDataElementsType `json:"hvacOverrunDescriptionDataElements,omitempty" eebus:"typ:elements,fct:hvacOverrunDescriptionListData"` - HvacSystemFunctionDataElements *HvacSystemFunctionDataElementsType `json:"hvacSystemFunctionDataElements,omitempty" eebus:"typ:elements,fct:hvacSystemFunctionListData"` - HvacSystemFunctionDescriptionDataElements *HvacSystemFunctionDescriptionDataElementsType `json:"hvacSystemFunctionDescriptionDataElements,omitempty" eebus:"typ:elements,fct:hvacSystemFunctionDescriptionListData"` - HvacSystemFunctionOperationModeRelationDataElements *HvacSystemFunctionOperationModeRelationDataElementsType `json:"hvacSystemFunctionOperationModeRelationDataElements,omitempty" eebus:"typ:elements,fct:hvacSystemFunctionOperationModeRelationListData"` - HvacSystemFunctionPowerSequenceRelationDataElements *HvacSystemFunctionPowerSequenceRelationDataElementsType `json:"hvacSystemFunctionPowerSequenceRelationDataElements,omitempty" eebus:"typ:elements,fct:hvacSystemFunctionPowerSequenceRelationListData"` - HvacSystemFunctionSetpointRelationDataElements *HvacSystemFunctionSetpointRelationDataElementsType `json:"hvacSystemFunctionSetpointRelationDataElements,omitempty" eebus:"typ:elements,fct:hvacSystemFunctionSetpointRelationListData"` - IdentificationDataElements *IdentificationDataElementsType `json:"identificationDataElements,omitempty" eebus:"typ:elements,fct:identificationListData"` - IncentiveDataElements *IncentiveDataElementsType `json:"incentiveDataElements,omitempty" eebus:"typ:elements,fct:incentiveListData"` - IncentiveDescriptionDataElements *IncentiveDescriptionDataElementsType `json:"incentiveDescriptionDataElements,omitempty" eebus:"typ:elements,fct:incentiveDescriptionListData"` - IncentiveTableConstraintsDataElements *IncentiveTableConstraintsDataElementsType `json:"incentiveTableConstraintsDataElements,omitempty" eebus:"typ:elements,fct:incentiveTableConstraintsData"` - IncentiveTableDataElements *IncentiveTableDataElementsType `json:"incentiveTableDataElements,omitempty" eebus:"typ:elements,fct:incentiveTableData"` - IncentiveTableDescriptionDataElements *IncentiveTableDescriptionDataElementsType `json:"incentiveTableDescriptionDataElements,omitempty" eebus:"typ:elements,fct:incentiveTableDescriptionData"` - LoadControlEventDataElements *LoadControlEventDataElementsType `json:"loadControlEventDataElements,omitempty" eebus:"typ:elements,fct:loadControlEventListData"` - LoadControlLimitConstraintsDataElements *LoadControlLimitConstraintsDataElementsType `json:"loadControlLimitConstraintsDataElements,omitempty" eebus:"typ:elements,fct:loadControlLimitConstraintsListData"` - LoadControlLimitDataElements *LoadControlLimitDataElementsType `json:"loadControlLimitDataElements,omitempty" eebus:"typ:elements,fct:loadControlLimitListData"` - LoadControlLimitDescriptionDataElements *LoadControlLimitDescriptionDataElementsType `json:"loadControlLimitDescriptionDataElements,omitempty" eebus:"typ:elements,fct:loadControlLimitDescriptionListData"` - LoadControlNodeDataElements *LoadControlNodeDataElementsType `json:"loadControlNodeDataElements,omitempty" eebus:"typ:elements,fct:loadControlNodeData"` - LoadControlStateDataElements *LoadControlStateDataElementsType `json:"loadControlStateDataElements,omitempty" eebus:"typ:elements,fct:loadControlStateListData"` - MeasurementConstraintsDataElements *MeasurementConstraintsDataElementsType `json:"measurementConstraintsDataElements,omitempty" eebus:"typ:elements,fct:measurementConstraintsListData"` - MeasurementDataElements *MeasurementDataElementsType `json:"measurementDataElements,omitempty" eebus:"typ:elements,fct:measurementListData"` - MeasurementDescriptionDataElements *MeasurementDescriptionDataElementsType `json:"measurementDescriptionDataElements,omitempty" eebus:"typ:elements,fct:measurementDescriptionListData"` - MeasurementSeriesDataElements *MeasurementSeriesDataElementsType `json:"measurementSeriesDataElements,omitempty" eebus:"typ:elements,fct:measurementSeriesListData"` - MeasurementThresholdRelationDataElements *MeasurementThresholdRelationDataElementsType `json:"measurementThresholdRelationDataElements,omitempty" eebus:"typ:elements,fct:measurementThresholdRelationListData"` - MessagingDataElements *MessagingDataElementsType `json:"messagingDataElements,omitempty" eebus:"typ:elements,fct:messagingListData"` - NetworkManagementAbortCallElements *NetworkManagementAbortCallElementsType `json:"networkManagementAbortCallElements,omitempty" eebus:"typ:elements,fct:networkManagementAbortCall"` - NetworkManagementAddNodeCallElements *NetworkManagementAddNodeCallElementsType `json:"networkManagementAddNodeCallElements,omitempty" eebus:"typ:elements,fct:networkManagementAddNodeCall"` - NetworkManagementDeviceDescriptionDataElements *NetworkManagementDeviceDescriptionDataElementsType `json:"networkManagementDeviceDescriptionDataElements,omitempty" eebus:"typ:elements,fct:networkManagementDeviceDescriptionListData"` - NetworkManagementDiscoverCallElements *NetworkManagementDiscoverCallElementsType `json:"networkManagementDiscoverCallElements,omitempty" eebus:"typ:elements,fct:networkManagementDiscoverCall"` - NetworkManagementEntityDescriptionDataElements *NetworkManagementEntityDescriptionDataElementsType `json:"networkManagementEntityDescriptionDataElements,omitempty" eebus:"typ:elements,fct:networkManagementEntityDescriptionListData"` - NetworkManagementFeatureDescriptionDataElements *NetworkManagementFeatureDescriptionDataElementsType `json:"networkManagementFeatureDescriptionDataElements,omitempty" eebus:"typ:elements,fct:networkManagementFeatureDescriptionListData"` - NetworkManagementJoiningModeDataElements *NetworkManagementJoiningModeDataElementsType `json:"networkManagementJoiningModeDataElements,omitempty" eebus:"typ:elements,fct:networkManagementJoiningModeData"` - NetworkManagementModifyNodeCallElements *NetworkManagementModifyNodeCallElementsType `json:"networkManagementModifyNodeCallElements,omitempty" eebus:"typ:elements,fct:networkManagementModifyNodeCall"` - NetworkManagementProcessStateDataElements *NetworkManagementProcessStateDataElementsType `json:"networkManagementProcessStateDataElements,omitempty" eebus:"typ:elements,fct:networkManagementProcessStateData"` - NetworkManagementRemoveNodeCallElements *NetworkManagementRemoveNodeCallElementsType `json:"networkManagementRemoveNodeCallElements,omitempty" eebus:"typ:elements,fct:networkManagementRemoveNodeCall"` - NetworkManagementReportCandidateDataElements *NetworkManagementReportCandidateDataElementsType `json:"networkManagementReportCandidateDataElements,omitempty" eebus:"typ:elements,fct:networkManagementReportCandidateData"` - NetworkManagementScanNetworkCallElements *NetworkManagementScanNetworkCallElementsType `json:"networkManagementScanNetworkCallElements,omitempty" eebus:"typ:elements,fct:networkManagementScanNetworkCall"` - NodeManagementBindingDataElements *NodeManagementBindingDataElementsType `json:"nodeManagementBindingDataElements,omitempty" eebus:"typ:elements,fct:nodeManagementBindingData"` - NodeManagementBindingDeleteCallElements *NodeManagementBindingDeleteCallElementsType `json:"nodeManagementBindingDeleteCallElements,omitempty" eebus:"typ:elements,fct:nodeManagementBindingDeleteCall"` - NodeManagementBindingRequestCallElements *NodeManagementBindingRequestCallElementsType `json:"nodeManagementBindingRequestCallElements,omitempty" eebus:"typ:elements,fct:nodeManagementBindingRequestCall"` - NodeManagementDestinationDataElements *NodeManagementDestinationDataElementsType `json:"nodeManagementDestinationDataElements,omitempty" eebus:"typ:elements,fct:nodeManagementDestinationListData"` - NodeManagementDetailedDiscoveryDataElements *NodeManagementDetailedDiscoveryDataElementsType `json:"nodeManagementDetailedDiscoveryDataElements,omitempty" eebus:"typ:elements,fct:nodeManagementDetailedDiscoveryData"` - NodeManagementSubscriptionDataElements *NodeManagementSubscriptionDataElementsType `json:"nodeManagementSubscriptionDataElements,omitempty" eebus:"typ:elements,fct:nodeManagementSubscriptionData"` - NodeManagementSubscriptionDeleteCallElements *NodeManagementSubscriptionDeleteCallElementsType `json:"nodeManagementSubscriptionDeleteCallElements,omitempty" eebus:"typ:elements,fct:nodeManagementSubscriptionDeleteCall"` - NodeManagementSubscriptionRequestCallElements *NodeManagementSubscriptionRequestCallElementsType `json:"nodeManagementSubscriptionRequestCallElements,omitempty" eebus:"typ:elements,fct:nodeManagementSubscriptionRequestCall"` - NodeManagementUseCaseDataElements *NodeManagementUseCaseDataElementsType `json:"nodeManagementUseCaseDataElements,omitempty" eebus:"typ:elements,fct:nodeManagementUseCaseData"` - OperatingConstraintsDurationDataElements *OperatingConstraintsDurationDataElementsType `json:"operatingConstraintsDurationDataElements,omitempty" eebus:"typ:elements,fct:operatingConstraintsDurationListData"` - OperatingConstraintsInterruptDataElements *OperatingConstraintsInterruptDataElementsType `json:"operatingConstraintsInterruptDataElements,omitempty" eebus:"typ:elements,fct:operatingConstraintsInterruptListData"` - OperatingConstraintsPowerDescriptionDataElements *OperatingConstraintsPowerDescriptionDataElementsType `json:"operatingConstraintsPowerDescriptionDataElements,omitempty" eebus:"typ:elements,fct:operatingConstraintsPowerDescriptionListData"` - OperatingConstraintsPowerLevelDataElements *OperatingConstraintsPowerLevelDataElementsType `json:"operatingConstraintsPowerLevelDataElements,omitempty" eebus:"typ:elements,fct:operatingConstraintsPowerLevelListData"` - OperatingConstraintsPowerRangeDataElements *OperatingConstraintsPowerRangeDataElementsType `json:"operatingConstraintsPowerRangeDataElements,omitempty" eebus:"typ:elements,fct:operatingConstraintsPowerRangeListData"` - OperatingConstraintsResumeImplicationDataElements *OperatingConstraintsResumeImplicationDataElementsType `json:"operatingConstraintsResumeImplicationDataElements,omitempty" eebus:"typ:elements,fct:operatingConstraintsResumeImplicationListData"` - PowerSequenceAlternativesRelationDataElements *PowerSequenceAlternativesRelationDataElementsType `json:"powerSequenceAlternativesRelationDataElements,omitempty" eebus:"typ:elements,fct:powerSequenceAlternativesRelationListData"` - PowerSequenceDescriptionDataElements *PowerSequenceDescriptionDataElementsType `json:"powerSequenceDescriptionDataElements,omitempty" eebus:"typ:elements,fct:powerSequenceDescriptionListData"` - PowerSequenceNodeScheduleInformationDataElements *PowerSequenceNodeScheduleInformationDataElementsType `json:"powerSequenceNodeScheduleInformationDataElements,omitempty" eebus:"typ:elements,fct:powerSequenceNodeScheduleInformationData"` - PowerSequencePriceCalculationRequestCallElements *PowerSequencePriceCalculationRequestCallElementsType `json:"powerSequencePriceCalculationRequestCallElements,omitempty" eebus:"typ:elements,fct:powerSequencePriceCalculationRequestCall"` - PowerSequencePriceDataElements *PowerSequencePriceDataElementsType `json:"powerSequencePriceDataElements,omitempty" eebus:"typ:elements,fct:powerSequencePriceListData"` - PowerSequenceScheduleConfigurationRequestCallElements *PowerSequenceScheduleConfigurationRequestCallElementsType `json:"powerSequenceScheduleConfigurationRequestCallElements,omitempty" eebus:"typ:elements,fct:powerSequenceScheduleConfigurationRequestCall"` - PowerSequenceScheduleConstraintsDataElements *PowerSequenceScheduleConstraintsDataElementsType `json:"powerSequenceScheduleConstraintsDataElements,omitempty" eebus:"typ:elements,fct:powerSequenceScheduleConstraintsListData"` - PowerSequenceScheduleDataElements *PowerSequenceScheduleDataElementsType `json:"powerSequenceScheduleDataElements,omitempty" eebus:"typ:elements,fct:powerSequenceScheduleListData"` - PowerSequenceSchedulePreferenceDataElements *PowerSequenceSchedulePreferenceDataElementsType `json:"powerSequenceSchedulePreferenceDataElements,omitempty" eebus:"typ:elements,fct:powerSequenceSchedulePreferenceListData"` - PowerSequenceStateDataElements *PowerSequenceStateDataElementsType `json:"powerSequenceStateDataElements,omitempty" eebus:"typ:elements,fct:powerSequenceStateListData"` - PowerTimeSlotScheduleConstraintsDataElements *PowerTimeSlotScheduleConstraintsDataElementsType `json:"powerTimeSlotScheduleConstraintsDataElements,omitempty" eebus:"typ:elements,fct:powerTimeSlotScheduleConstraintsListData"` - PowerTimeSlotScheduleDataElements *PowerTimeSlotScheduleDataElementsType `json:"powerTimeSlotScheduleDataElements,omitempty" eebus:"typ:elements,fct:powerTimeSlotScheduleListData"` - PowerTimeSlotValueDataElements *PowerTimeSlotValueDataElementsType `json:"powerTimeSlotValueDataElements,omitempty" eebus:"typ:elements,fct:powerTimeSlotValueListData"` - SensingDataElements *SensingDataElementsType `json:"sensingDataElements,omitempty" eebus:"typ:elements,fct:sensingListData"` - SensingDescriptionDataElements *SensingDescriptionDataElementsType `json:"sensingDescriptionDataElements,omitempty" eebus:"typ:elements,fct:sensingDescriptionData"` - SessionIdentificationDataElements *SessionIdentificationDataElementsType `json:"sessionIdentificationDataElements,omitempty" eebus:"typ:elements,fct:sessionIdentificationData"` - SessionMeasurementRelationDataElements *SessionMeasurementRelationDataElementsType `json:"sessionMeasurementRelationDataElements,omitempty" eebus:"typ:elements,fct:sessionMeasurementRelationData"` - SetpointConstraintsDataElements *SetpointConstraintsDataElementsType `json:"setpointConstraintsDataElements,omitempty" eebus:"typ:elements,fct:setpointConstraintsListData"` - SetpointDataElements *SetpointDataElementsType `json:"setpointDataElements,omitempty" eebus:"typ:elements,fct:setpointListData"` - SetpointDescriptionDataElements *SetpointDescriptionDataElementsType `json:"setpointDescriptionDataElements,omitempty" eebus:"typ:elements,fct:"` - SmartEnergyManagementPsConfigurationRequestCallElements *SmartEnergyManagementPsConfigurationRequestCallElementsType `json:"smartEnergyManagementPsConfigurationRequestCallElements,omitempty" eebus:"typ:elements,fct:smartEnergyManagementPsConfigurationRequestCall"` - SmartEnergyManagementPsDataElements *SmartEnergyManagementPsDataElementsType `json:"smartEnergyManagementPsDataElements,omitempty" eebus:"typ:elements,fct:smartEnergyManagementPsData"` - SmartEnergyManagementPsPriceCalculationRequestCallElements *SmartEnergyManagementPsPriceCalculationRequestCallElementsType `json:"smartEnergyManagementPsPriceCalculationRequestCallElements,omitempty" eebus:"typ:elements,fct:smartEnergyManagementPsPriceCalculationRequestCall"` - SmartEnergyManagementPsPriceDataElements *SmartEnergyManagementPsPriceDataElementsType `json:"smartEnergyManagementPsPriceDataElements,omitempty" eebus:"typ:elements,fct:smartEnergyManagementPsPriceData"` - SpecificationVersionDataElements *SpecificationVersionDataElementsType `json:"specificationVersionDataElements,omitempty" eebus:"typ:elements,fct:specificationVersionListData"` - StateInformationDataElements *StateInformationDataElementsType `json:"stateInformationDataElements,omitempty" eebus:"typ:elements,fct:stateInformationListData"` - SubscriptionManagementDeleteCallElements *SubscriptionManagementDeleteCallElementsType `json:"subscriptionManagementDeleteCallElements,omitempty" eebus:"typ:elements,fct:subscriptionManagementDeleteCall"` - SubscriptionManagementEntryDataElements *SubscriptionManagementEntryDataElementsType `json:"subscriptionManagementEntryDataElements,omitempty" eebus:"typ:elements,fct:subscriptionManagementEntryListData"` - SubscriptionManagementRequestCallElements *SubscriptionManagementRequestCallElementsType `json:"subscriptionManagementRequestCallElements,omitempty" eebus:"typ:elements,fct:subscriptionManagementRequestCall"` - SupplyConditionDataElements *SupplyConditionDataElementsType `json:"supplyConditionDataElements,omitempty" eebus:"typ:elements,fct:supplyConditionListData"` - SupplyConditionDescriptionDataElements *SupplyConditionDescriptionDataElementsType `json:"supplyConditionDescriptionDataElements,omitempty" eebus:"typ:elements,fct:supplyConditionDescriptionListData"` - SupplyConditionThresholdRelationDataElements *SupplyConditionThresholdRelationDataElementsType `json:"supplyConditionThresholdRelationDataElements,omitempty" eebus:"typ:elements,fct:supplyConditionThresholdRelationListData"` - TariffBoundaryRelationDataElements *TariffBoundaryRelationDataElementsType `json:"tariffBoundaryRelationDataElements,omitempty" eebus:"typ:elements,fct:tariffBoundaryRelationListData"` - TariffDataElements *TariffDataElementsType `json:"tariffDataElements,omitempty" eebus:"typ:elements,fct:tariffListData"` - TariffDescriptionDataElements *TariffDescriptionDataElementsType `json:"tariffDescriptionDataElements,omitempty" eebus:"typ:elements,fct:tariffDescriptionListData"` - TariffOverallConstraintsDataElements *TariffOverallConstraintsDataElementsType `json:"tariffOverallConstraintsDataElements,omitempty" eebus:"typ:elements,fct:tariffOverallConstraintsData"` - TariffTierRelationDataElements *TariffTierRelationDataElementsType `json:"tariffTierRelationDataElements,omitempty" eebus:"typ:elements,fct:tariffTierRelationListData"` - TaskManagementJobDataElements *TaskManagementJobDataElementsType `json:"taskManagementJobDataElements,omitempty" eebus:"typ:elements,fct:taskManagementJobListData"` - TaskManagementJobDescriptionDataElements *TaskManagementJobDescriptionDataElementsType `json:"taskManagementJobDescriptionDataElements,omitempty" eebus:"typ:elements,fct:taskManagementJobDescriptionListData"` - TaskManagementJobRelationDataElements *TaskManagementJobRelationDataElementsType `json:"taskManagementJobRelationDataElements,omitempty" eebus:"typ:elements,fct:taskManagementJobRelationListData"` - TaskManagementOverviewDataElements *TaskManagementOverviewDataElementsType `json:"taskManagementOverviewDataElements,omitempty" eebus:"typ:elements,fct:taskManagementOverviewData"` - ThresholdConstraintsDataElements *ThresholdConstraintsDataElementsType `json:"thresholdConstraintsDataElements,omitempty" eebus:"typ:elements,fct:thresholdConstraintsListData"` - ThresholdDataElements *ThresholdDataElementsType `json:"thresholdDataElements,omitempty" eebus:"typ:elements,fct:thresholdListData"` - ThresholdDescriptionDataElements *ThresholdDescriptionDataElementsType `json:"thresholdDescriptionDataElements,omitempty" eebus:"typ:elements,fct:thresholdDescriptionListData"` - TierBoundaryDataElements *TierBoundaryDataElementsType `json:"tierBoundaryDataElements,omitempty" eebus:"typ:elements,fct:tierBoundaryListData"` - TierBoundaryDescriptionDataElements *TierBoundaryDescriptionDataElementsType `json:"tierBoundaryDescriptionDataElements,omitempty" eebus:"typ:elements,fct:tierBoundaryDescriptionListData"` - TierDataElements *TierDataElementsType `json:"tierDataElements,omitempty" eebus:"typ:elements,fct:tierListData"` - TierDescriptionDataElements *TierDescriptionDataElementsType `json:"tierDescriptionDataElements,omitempty" eebus:"typ:elements,fct:tierDescriptionListData"` - TierIncentiveRelationDataElements *TierIncentiveRelationDataElementsType `json:"tierIncentiveRelationDataElements,omitempty" eebus:"typ:elements,fct:tierIncentiveRelationListData"` - TimeDistributorDataElements *TimeDistributorDataElementsType `json:"timeDistributorDataElements,omitempty" eebus:"typ:elements,fct:timeDistributorData"` - TimeDistributorEnquiryCallElements *TimeDistributorEnquiryCallElementsType `json:"timeDistributorEnquiryCallElements,omitempty" eebus:"typ:elements,fct:timeDistributorEnquiryCall"` - TimeInformationDataElements *TimeInformationDataElementsType `json:"timeInformationDataElements,omitempty" eebus:"typ:elements,fct:timeInformationData"` - TimePrecisionDataElements *TimePrecisionDataElementsType `json:"timePrecisionDataElements,omitempty" eebus:"typ:elements,fct:timePrecisionData"` - TimeSeriesConstraintsDataElements *TimeSeriesConstraintsDataElementsType `json:"timeSeriesConstraintsDataElements,omitempty" eebus:"typ:elements,fct:timeSeriesConstraintsListData"` - TimeSeriesDataElements *TimeSeriesDataElementsType `json:"timeSeriesDataElements,omitempty" eebus:"typ:elements,fct:timeSeriesListData"` - TimeSeriesDescriptionDataElements *TimeSeriesDescriptionDataElementsType `json:"timeSeriesDescriptionDataElements,omitempty" eebus:"typ:elements,fct:timeSeriesDescriptionListData"` - TimeTableConstraintsDataElements *TimeTableConstraintsDataElementsType `json:"timeTableConstraintsDataElements,omitempty" eebus:"typ:elements,fct:timeTableConstraintsListData"` - TimeTableDataElements *TimeTableDataElementsType `json:"timeTableDataElements,omitempty" eebus:"typ:elements,fct:timeTableListData"` - TimeTableDescriptionDataElements *TimeTableDescriptionDataElementsType `json:"timeTableDescriptionDataElements,omitempty" eebus:"typ:elements,fct:timeTableDescriptionListData"` - UseCaseInformationDataElements *UseCaseInformationDataElementsType `json:"useCaseInformationDataElements,omitempty" eebus:"typ:elements,fct:useCaseInformationListData"` -} - -type CmdControlType struct { - Delete *ElementTagType `json:"delete,omitempty"` - Partial *ElementTagType `json:"partial,omitempty"` -} - -type CmdType struct { - // CmdOptionGroup - Function *FunctionType `json:"function,omitempty"` - Filter []FilterType `json:"filter,omitempty"` - - // DataChoiceGroup - ActuatorLevelData *ActuatorLevelDataType `json:"actuatorLevelData,omitempty" eebus:"fct:actuatorLevelData"` - ActuatorLevelDescriptionData *ActuatorLevelDescriptionDataType `json:"actuatorLevelDescriptionData,omitempty" eebus:"fct:actuatorLevelDescriptionData"` - ActuatorSwitchData *ActuatorSwitchDataType `json:"actuatorSwitchData,omitempty" eebus:"fct:actuatorSwitchData"` - ActuatorSwitchDescriptionData *ActuatorSwitchDescriptionDataType `json:"actuatorSwitchDescriptionData,omitempty" eebus:"fct:actuatorSwitchDescriptionData"` - AlarmListData *AlarmListDataType `json:"alarmListData,omitempty" eebus:"fct:alarmListData"` - BillConstraintsListData *BillConstraintsListDataType `json:"billConstraintsListData,omitempty" eebus:"fct:billConstraintsListData"` - BillDescriptionListData *BillDescriptionListDataType `json:"billDescriptionListData,omitempty" eebus:"fct:billDescriptionListData"` - BillListData *BillListDataType `json:"billListData,omitempty" eebus:"fct:billListData"` - BindingManagementDeleteCall *BindingManagementDeleteCallType `json:"bindingManagementDeleteCall,omitempty" eebus:"fct:bindingManagementDeleteCall"` - BindingManagementEntryListData *BindingManagementEntryListDataType `json:"bindingManagementEntryListData,omitempty" eebus:"fct:bindingManagementEntryListData"` - BindingManagementRequestCall *BindingManagementRequestCallType `json:"bindingManagementRequestCall,omitempty" eebus:"fct:bindingManagementRequestCall"` - CommodityListData *CommodityListDataType `json:"commodityListData,omitempty" eebus:"fct:commodityListData"` - DataTunnelingCall *DataTunnelingCallType `json:"dataTunnelingCall,omitempty" eebus:"fct:dataTunnelingCall"` - DeviceClassificationManufacturerData *DeviceClassificationManufacturerDataType `json:"deviceClassificationManufacturerData,omitempty" eebus:"fct:deviceClassificationManufacturerData"` - DeviceClassificationUserData *DeviceClassificationUserDataType `json:"deviceClassificationUserData,omitempty" eebus:"fct:deviceClassificationUserData"` - DeviceConfigurationKeyValueConstraintsListData *DeviceConfigurationKeyValueConstraintsListDataType `json:"deviceConfigurationKeyValueConstraintsListData,omitempty" eebus:"fct:deviceConfigurationKeyValueConstraintsListData"` - DeviceConfigurationKeyValueDescriptionListData *DeviceConfigurationKeyValueDescriptionListDataType `json:"deviceConfigurationKeyValueDescriptionListData,omitempty" eebus:"fct:deviceConfigurationKeyValueDescriptionListData"` - DeviceConfigurationKeyValueListData *DeviceConfigurationKeyValueListDataType `json:"deviceConfigurationKeyValueListData,omitempty" eebus:"fct:deviceConfigurationKeyValueListData"` - DeviceDiagnosisHeartbeatData *DeviceDiagnosisHeartbeatDataType `json:"deviceDiagnosisHeartbeatData,omitempty" eebus:"fct:deviceDiagnosisHeartbeatData"` - DeviceDiagnosisServiceData *DeviceDiagnosisServiceDataType `json:"deviceDiagnosisServiceData,omitempty" eebus:"fct:deviceDiagnosisServiceData"` - DeviceDiagnosisStateData *DeviceDiagnosisStateDataType `json:"deviceDiagnosisStateData,omitempty" eebus:"fct:deviceDiagnosisStateData"` - DirectControlActivityListData *DirectControlActivityListDataType `json:"directControlActivityListData,omitempty" eebus:"fct:directControlActivityListData"` - DirectControlDescriptionData *DirectControlDescriptionDataType `json:"directControlDescriptionData,omitempty" eebus:"fct:directControlDescriptionData"` - ElectricalConnectionDescriptionListData *ElectricalConnectionDescriptionListDataType `json:"electricalConnectionDescriptionListData,omitempty" eebus:"fct:electricalConnectionDescriptionListData"` - ElectricalConnectionParameterDescriptionListData *ElectricalConnectionParameterDescriptionListDataType `json:"electricalConnectionParameterDescriptionListData,omitempty" eebus:"fct:electricalConnectionParameterDescriptionListData"` - ElectricalConnectionPermittedValueSetListData *ElectricalConnectionPermittedValueSetListDataType `json:"electricalConnectionPermittedValueSetListData,omitempty" eebus:"fct:electricalConnectionPermittedValueSetListData"` - ElectricalConnectionStateListData *ElectricalConnectionStateListDataType `json:"electricalConnectionStateListData,omitempty" eebus:"fct:electricalConnectionStateListData"` - ElectricalConnectionCharacteristicData *ElectricalConnectionCharacteristicDataType `json:"electricalConnectionCharacteristicData,omitempty" eebus:"fct:electricalConnectionCharacteristicData"` - ElectricalConnectionCharacteristicListData *ElectricalConnectionCharacteristicListDataType `json:"electricalConnectionCharacteristicListData,omitempty" eebus:"fct:electricalConnectionCharacteristicListData"` - HvacOperationModeDescriptionListData *HvacOperationModeDescriptionListDataType `json:"hvacOperationModeDescriptionListData,omitempty" eebus:"fct:hvacOperationModeDescriptionListData"` - HvacOverrunDescriptionListData *HvacOverrunDescriptionListDataType `json:"hvacOverrunDescriptionListData,omitempty" eebus:"fct:hvacOverrunDescriptionListData"` - HvacOverrunListData *HvacOverrunListDataType `json:"hvacOverrunListData,omitempty" eebus:"fct:hvacOverrunListData"` - HvacSystemFunctionDescriptionListData *HvacSystemFunctionDescriptionListDataType `json:"hvacSystemFunctionDescriptionListData,omitempty" eebus:"fct:hvacSystemFunctionDescriptionListData"` - HvacSystemFunctionListData *HvacSystemFunctionListDataType `json:"hvacSystemFunctionListData,omitempty" eebus:"fct:hvacSystemFunctionListData"` - HvacSystemFunctionOperationModeRelationListData *HvacSystemFunctionOperationModeRelationListDataType `json:"hvacSystemFunctionOperationModeRelationListData,omitempty" eebus:"fct:hvacSystemFunctionOperationModeRelationListData"` - HvacSystemFunctionPowerSequenceRelationListData *HvacSystemFunctionPowerSequenceRelationListDataType `json:"hvacSystemFunctionPowerSequenceRelationListData,omitempty" eebus:"fct:hvacSystemFunctionPowerSequenceRelationListData"` - HvacSystemFunctionSetPointRelationListData *HvacSystemFunctionSetpointRelationListDataType `json:"hvacSystemFunctionSetpointRelationListData,omitempty" eebus:"fct:hvacSystemFunctionSetpointRelationListData"` - IdentificationListData *IdentificationListDataType `json:"identificationListData,omitempty" eebus:"fct:identificationListData"` - IncentiveDescriptionListData *IncentiveDescriptionListDataType `json:"incentiveDescriptionListData,omitempty" eebus:"fct:incentiveDescriptionListData"` - IncentiveListData *IncentiveListDataType `json:"incentiveListData,omitempty" eebus:"fct:incentiveListData"` - IncentiveTableConstraintsData *IncentiveTableConstraintsDataType `json:"incentiveTableConstraintsData,omitempty" eebus:"fct:incentiveTableConstraintsData"` - IncentiveTableData *IncentiveTableDataType `json:"incentiveTableData,omitempty" eebus:"fct:incentiveTableData"` - IncentiveTableDescriptionData *IncentiveTableDescriptionDataType `json:"incentiveTableDescriptionData,omitempty" eebus:"fct:incentiveTableDescriptionData"` - LoadControlEventListData *LoadControlEventListDataType `json:"loadControlEventListData,omitempty" eebus:"fct:loadControlEventListData"` - LoadControlLimitConstraintsListData *LoadControlLimitConstraintsListDataType `json:"loadControlLimitConstraintsListData,omitempty" eebus:"fct:loadControlLimitConstraintsListData"` - LoadControlLimitDescriptionListData *LoadControlLimitDescriptionListDataType `json:"loadControlLimitDescriptionListData,omitempty" eebus:"fct:loadControlLimitDescriptionListData"` - LoadControlLimitListData *LoadControlLimitListDataType `json:"loadControlLimitListData,omitempty" eebus:"fct:loadControlLimitListData"` - LoadControlNodeData *LoadControlNodeDataType `json:"loadControlNodeData,omitempty" eebus:"fct:loadControlNodeData"` - LoadControlStateListData *LoadControlStateListDataType `json:"loadControlStateListData,omitempty" eebus:"fct:loadControlStateListData"` - MeasurementConstraintsListData *MeasurementConstraintsListDataType `json:"measurementConstraintsListData,omitempty" eebus:"fct:measurementConstraintsListData"` - MeasurementDescriptionListData *MeasurementDescriptionListDataType `json:"measurementDescriptionListData,omitempty" eebus:"fct:measurementDescriptionListData"` - MeasurementListData *MeasurementListDataType `json:"measurementListData,omitempty" eebus:"fct:measurementListData"` - MeasurementSeriesListData *MeasurementSeriesListDataType `json:"measurementSeriesListData,omitempty" eebus:"fct:measurementSeriesListData"` - MeasurementThresholdRelationListData *MeasurementThresholdRelationListDataType `json:"measurementThresholdRelationListData,omitempty" eebus:"fct:measurementThresholdRelationListData"` - MessagingListData *MessagingListDataType `json:"messagingListData,omitempty" eebus:"fct:messagingListData"` - NetworkManagementAbortCall *NetworkManagementAbortCallType `json:"networkManagementAbortCall,omitempty" eebus:"fct:networkManagementAbortCall"` - NetworkManagementAddNodeCall *NetworkManagementAddNodeCallType `json:"networkManagementAddNodeCall,omitempty" eebus:"fct:networkManagementAddNodeCall"` - NetworkManagementDeviceDescriptionListData *NetworkManagementDeviceDescriptionListDataType `json:"networkManagementDeviceDescriptionListData,omitempty" eebus:"fct:networkManagementDeviceDescriptionListData"` - NetworkManagementDiscoverCall *NetworkManagementDiscoverCallType `json:"networkManagementDiscoverCall,omitempty" eebus:"fct:networkManagementDiscoverCall"` - NetworkManagementEntityDescriptionListData *NetworkManagementEntityDescriptionListDataType `json:"networkManagementEntityDescriptionListData,omitempty" eebus:"fct:networkManagementEntityDescriptionListData"` - NetworkManagementFeatureDescriptionListData *NetworkManagementFeatureDescriptionListDataType `json:"networkManagementFeatureDescriptionListData,omitempty" eebus:"fct:networkManagementFeatureDescriptionListData"` - NetworkManagementJoiningModeData *NetworkManagementJoiningModeDataType `json:"networkManagementJoiningModeData,omitempty" eebus:"fct:networkManagementJoiningModeData"` - NetworkManagementModifyNodeCall *NetworkManagementModifyNodeCallType `json:"networkManagementModifyNodeCall,omitempty" eebus:"fct:networkManagementModifyNodeCall"` - NetworkManagementProcessStateData *NetworkManagementProcessStateDataType `json:"networkManagementProcessStateData,omitempty" eebus:"fct:networkManagementProcessStateData"` - NetworkManagementRemoveNodeCall *NetworkManagementRemoveNodeCallType `json:"networkManagementRemoveNodeCall,omitempty" eebus:"fct:networkManagementRemoveNodeCall"` - NetworkManagementReportCandidateData *NetworkManagementReportCandidateDataType `json:"networkManagementReportCandidateData,omitempty" eebus:"fct:networkManagementReportCandidateData"` - NetworkManagementScanNetworkCall *NetworkManagementScanNetworkCallType `json:"networkManagementScanNetworkCall,omitempty" eebus:"fct:networkManagementScanNetworkCall"` - NodeManagementBindingData *NodeManagementBindingDataType `json:"nodeManagementBindingData,omitempty" eebus:"fct:nodeManagementBindingData"` - NodeManagementBindingDeleteCall *NodeManagementBindingDeleteCallType `json:"nodeManagementBindingDeleteCall,omitempty" eebus:"fct:nodeManagementBindingDeleteCall"` - NodeManagementBindingRequestCall *NodeManagementBindingRequestCallType `json:"nodeManagementBindingRequestCall,omitempty" eebus:"fct:nodeManagementBindingRequestCall"` - NodeManagementDestinationListData *NodeManagementDestinationListDataType `json:"nodeManagementDestinationListData,omitempty" eebus:"fct:nodeManagementDestinationListData"` - NodeManagementDetailedDiscoveryData *NodeManagementDetailedDiscoveryDataType `json:"nodeManagementDetailedDiscoveryData,omitempty" eebus:"fct:nodeManagementDetailedDiscoveryData"` - NodeManagementSubscriptionData *NodeManagementSubscriptionDataType `json:"nodeManagementSubscriptionData,omitempty" eebus:"fct:nodeManagementSubscriptionData"` - NodeManagementSubscriptionDeleteCall *NodeManagementSubscriptionDeleteCallType `json:"nodeManagementSubscriptionDeleteCall,omitempty" eebus:"fct:nodeManagementSubscriptionDeleteCall"` - NodeManagementSubscriptionRequestCall *NodeManagementSubscriptionRequestCallType `json:"nodeManagementSubscriptionRequestCall,omitempty" eebus:"fct:nodeManagementSubscriptionRequestCall"` - NodeManagementUseCaseData *NodeManagementUseCaseDataType `json:"nodeManagementUseCaseData,omitempty" eebus:"fct:nodeManagementUseCaseData"` - OperatingConstraintsDurationListData *OperatingConstraintsDurationListDataType `json:"operatingConstraintsDurationListData,omitempty" eebus:"fct:operatingConstraintsDurationListData"` - OperatingConstraintsInterruptListData *OperatingConstraintsInterruptListDataType `json:"operatingConstraintsInterruptListData,omitempty" eebus:"fct:operatingConstraintsInterruptListData"` - OperatingConstraintsPowerDescriptionListData *OperatingConstraintsPowerDescriptionListDataType `json:"operatingConstraintsPowerDescriptionListData,omitempty" eebus:"fct:operatingConstraintsPowerDescriptionListData"` - OperatingConstraintsPowerLevelListData *OperatingConstraintsPowerLevelListDataType `json:"operatingConstraintsPowerLevelListData,omitempty" eebus:"fct:operatingConstraintsPowerLevelListData"` - OperatingConstraintsPowerRangeListData *OperatingConstraintsPowerRangeListDataType `json:"operatingConstraintsPowerRangeListData,omitempty" eebus:"fct:operatingConstraintsPowerRangeListData"` - OperatingConstraintsResumeImplicationListData *OperatingConstraintsResumeImplicationListDataType `json:"operatingConstraintsResumeImplicationListData,omitempty" eebus:"fct:operatingConstraintsResumeImplicationListData"` - PowerSequenceAlternativesRelationListData *PowerSequenceAlternativesRelationListDataType `json:"powerSequenceAlternativesRelationListData,omitempty" eebus:"fct:powerSequenceAlternativesRelationListData"` - PowerSequenceDescriptionListData *PowerSequenceDescriptionListDataType `json:"powerSequenceDescriptionListData,omitempty" eebus:"fct:powerSequenceDescriptionListData"` - PowerSequenceNodeScheduleInformationData *PowerSequenceNodeScheduleInformationDataType `json:"powerSequenceNodeScheduleInformationData,omitempty" eebus:"fct:powerSequenceNodeScheduleInformationData"` - PowerSequencePriceCalculationRequestCall *PowerSequencePriceCalculationRequestCallType `json:"powerSequencePriceCalculationRequestCall,omitempty" eebus:"fct:powerSequencePriceCalculationRequestCall"` - PowerSequencePriceListData *PowerSequencePriceListDataType `json:"powerSequencePriceListData,omitempty" eebus:"fct:powerSequencePriceListData"` - PowerSequenceScheduleConfigurationRequestCall *PowerSequenceScheduleConfigurationRequestCallType `json:"powerSequenceScheduleConfigurationRequestCall,omitempty" eebus:"fct:powerSequenceScheduleConfigurationRequestCall"` - PowerSequenceScheduleConstraintsListData *PowerSequenceScheduleConstraintsListDataType `json:"powerSequenceScheduleConstraintsListData,omitempty" eebus:"fct:powerSequenceScheduleConstraintsListData"` - PowerSequenceScheduleListData *PowerSequenceScheduleListDataType `json:"powerSequenceScheduleListData,omitempty" eebus:"fct:powerSequenceScheduleListData"` - PowerSequenceSchedulePreferenceListData *PowerSequenceSchedulePreferenceListDataType `json:"powerSequenceSchedulePreferenceListData,omitempty" eebus:"fct:powerSequenceSchedulePreferenceListData"` - PowerSequenceStateListData *PowerSequenceStateListDataType `json:"powerSequenceStateListData,omitempty" eebus:"fct:powerSequenceStateListData"` - PowerTimeSlotScheduleConstraintsListData *PowerTimeSlotScheduleConstraintsListDataType `json:"powerTimeSlotScheduleConstraintsListData,omitempty" eebus:"fct:powerTimeSlotScheduleConstraintsListData"` - PowerTimeSlotScheduleListData *PowerTimeSlotScheduleListDataType `json:"powerTimeSlotScheduleListData,omitempty" eebus:"fct:powerTimeSlotScheduleListData"` - PowerTimeSlotValueListData *PowerTimeSlotValueListDataType `json:"powerTimeSlotValueListData,omitempty" eebus:"fct:powerTimeSlotValueListData"` - ResultData *ResultDataType `json:"resultData,omitempty" eebus:"fct:resultData"` - SensingDescriptionData *SensingDescriptionDataType `json:"sensingDescriptionData,omitempty" eebus:"fct:sensingDescriptionData"` - SensingListData *SensingListDataType `json:"sensingListData,omitempty" eebus:"fct:sensingListData"` - SessionIdentificationListData *SessionIdentificationListDataType `json:"sessionIdentificationListData,omitempty" eebus:"fct:sessionIdentificationListData"` - SessionMeasurementRelationListData *SessionMeasurementRelationListDataType `json:"sessionMeasurementRelationListData,omitempty" eebus:"fct:sessionMeasurementRelationListData"` - SetpointConstraintsListData *SetpointConstraintsListDataType `json:"setpointConstraintsListData,omitempty" eebus:"fct:setpointConstraintsListData"` - SetpointDescriptionListData *SetpointDescriptionListDataType `json:"setpointDescriptionListData,omitempty" eebus:"fct:setpointDescriptionListData"` - SetpointListData *SetpointListDataType `json:"setpointListData,omitempty" eebus:"fct:setpointListData"` - SmartEnergyManagementPsConfigurationRequestCall *SmartEnergyManagementPsConfigurationRequestCallType `json:"smartEnergyManagementPsConfigurationRequestCall,omitempty" eebus:"fct:smartEnergyManagementPsConfigurationRequestCall"` - SmartEnergyManagementPsData *SmartEnergyManagementPsDataType `json:"smartEnergyManagementPsData,omitempty" eebus:"fct:smartEnergyManagementPsData"` - SmartEnergyManagementPsPriceCalculationRequestCall *SmartEnergyManagementPsPriceCalculationRequestCallType `json:"smartEnergyManagementPsPriceCalculationRequestCall,omitempty" eebus:"fct:smartEnergyManagementPsPriceCalculationRequestCall"` - SmartEnergyManagementPsPriceData *SmartEnergyManagementPsPriceDataType `json:"smartEnergyManagementPsPriceData,omitempty" eebus:"fct:smartEnergyManagementPsPriceData"` - SpecificationVersionListData *SpecificationVersionListDataType `json:"specificationVersionListData,omitempty" eebus:"fct:specificationVersionListData"` - StateInformationListData *StateInformationListDataType `json:"stateInformationListData,omitempty" eebus:"fct:stateInformationListData"` - SubscriptionManagementDeleteCall *SubscriptionManagementDeleteCallType `json:"subscriptionManagementDeleteCall,omitempty" eebus:"fct:subscriptionManagementDeleteCall"` - SubscriptionManagementEntryListData *SubscriptionManagementEntryListDataType `json:"subscriptionManagementEntryListData,omitempty" eebus:"fct:subscriptionManagementEntryListData"` - SubscriptionManagementRequestCall *SubscriptionManagementRequestCallType `json:"subscriptionManagementRequestCall,omitempty" eebus:"fct:subscriptionManagementRequestCall"` - SupplyConditionDescriptionListData *SupplyConditionDescriptionListDataType `json:"supplyConditionDescriptionListData,omitempty" eebus:"fct:supplyConditionDescriptionListData"` - SupplyConditionListData *SupplyConditionListDataType `json:"supplyConditionListData,omitempty" eebus:"fct:supplyConditionListData"` - SupplyConditionThresholdRelationListData *SupplyConditionThresholdRelationListDataType `json:"supplyConditionThresholdRelationListData,omitempty" eebus:"fct:supplyConditionThresholdRelationListData"` - TariffBoundaryRelationListData *TariffBoundaryRelationListDataType `json:"tariffBoundaryRelationListData,omitempty" eebus:"fct:tariffBoundaryRelationListData"` - TariffDescriptionListData *TariffDescriptionListDataType `json:"tariffDescriptionListData,omitempty" eebus:"fct:tariffDescriptionListData"` - TariffListData *TariffListDataType `json:"tariffListData,omitempty" eebus:"fct:tariffListData"` - TariffOverallConstraintsData *TariffOverallConstraintsDataType `json:"tariffOverallConstraintsData,omitempty" eebus:"fct:tariffOverallConstraintsData"` - TariffTierRelationListData *TariffTierRelationListDataType `json:"tariffTierRelationListData,omitempty" eebus:"fct:tariffTierRelationListData"` - TaskManagementJobDescriptionListData *TaskManagementJobDescriptionListDataType `json:"taskManagementJobDescriptionListData,omitempty" eebus:"fct:taskManagementJobDescriptionListData"` - TaskManagementJobListData *TaskManagementJobListDataType `json:"taskManagementJobListData,omitempty" eebus:"fct:taskManagementJobListData"` - TaskManagementJobRelationListData *TaskManagementJobRelationListDataType `json:"taskManagementJobRelationListData,omitempty" eebus:"fct:taskManagementJobRelationListData"` - TaskManagementOverviewData *TaskManagementOverviewDataType `json:"taskManagementOverviewData,omitempty" eebus:"fct:taskManagementOverviewData"` - ThresholdConstraintsListData *ThresholdConstraintsListDataType `json:"thresholdConstraintsListData,omitempty" eebus:"fct:thresholdConstraintsListData"` - ThresholdDescriptionListData *ThresholdDescriptionListDataType `json:"thresholdDescriptionListData,omitempty" eebus:"fct:thresholdDescriptionListData"` - ThresholdListData *ThresholdListDataType `json:"thresholdListData,omitempty" eebus:"fct:thresholdListData"` - TierBoundaryDescriptionListData *TierBoundaryDescriptionListDataType `json:"tierBoundaryDescriptionListData,omitempty" eebus:"fct:tierBoundaryDescriptionListData"` - TierBoundaryListData *TierBoundaryListDataType `json:"tierBoundaryListData,omitempty" eebus:"fct:tierBoundaryListData"` - TierDescriptionListData *TierDescriptionListDataType `json:"tierDescriptionListData,omitempty" eebus:"fct:tierDescriptionListData"` - TierIncentiveRelationListData *TierIncentiveRelationListDataType `json:"tierIncentiveRelationListData,omitempty" eebus:"fct:tierIncentiveRelationListData"` - TierListData *TierListDataType `json:"tierListData,omitempty" eebus:"fct:tierListData"` - TimeDistributorData *TimeDistributorDataType `json:"timeDistributorData,omitempty" eebus:"fct:timeDistributorData"` - TimeDistributorEnquiryCall *TimeDistributorEnquiryCallType `json:"timeDistributorEnquiryCall,omitempty" eebus:"fct:timeDistributorEnquiryCall"` - TimeInformationData *TimeInformationDataType `json:"timeInformationData,omitempty" eebus:"fct:timeInformationData"` - TimePrecisionData *TimePrecisionDataType `json:"timePrecisionData,omitempty" eebus:"fct:timePrecisionData"` - TimeSeriesConstraintsListData *TimeSeriesConstraintsListDataType `json:"timeSeriesConstraintsListData,omitempty" eebus:"fct:timeSeriesConstraintsListData"` - TimeSeriesDescriptionListData *TimeSeriesDescriptionListDataType `json:"timeSeriesDescriptionListData,omitempty" eebus:"fct:timeSeriesDescriptionListData"` - TimeSeriesListData *TimeSeriesListDataType `json:"timeSeriesListData,omitempty" eebus:"fct:timeSeriesListData"` - TimeTableConstraintsListData *TimeTableConstraintsListDataType `json:"timeTableConstraintsListData,omitempty" eebus:"fct:timeTableConstraintsListData"` - TimeTableDescriptionListData *TimeTableDescriptionListDataType `json:"timeTableDescriptionListData,omitempty" eebus:"fct:timeTableDescriptionListData"` - TimeTableListData *TimeTableListDataType `json:"timeTableListData,omitempty" eebus:"fct:timeTableListData"` - UseCaseInformationListData *UseCaseInformationListDataType `json:"useCaseInformationListData,omitempty" eebus:"fct:useCaseInformationListData"` - - // DataExtendGroup - ManufacturerSpecificExtension *string `json:"manufacturerSpecificExtension,omitempty"` - LastUpdateAt *AbsoluteOrRelativeTimeType `json:"lastUpdateAt,omitempty"` -} diff --git a/spine/model/commandframe_additions.go b/spine/model/commandframe_additions.go deleted file mode 100644 index a8fcaf7b..00000000 --- a/spine/model/commandframe_additions.go +++ /dev/null @@ -1,297 +0,0 @@ -package model - -import ( - "errors" - "fmt" - "reflect" - - "github.com/enbility/eebus-go/util" -) - -func (r *MsgCounterType) String() string { - if r == nil { - return "" - } - return fmt.Sprintf("%d", *r) -} - -// FilterData stores the function field name and -// selector field name for a function -type FilterData struct { - Elements any - Selector any - Function *FunctionType -} - -func (f *FilterData) SelectorMatch(item any) bool { - if f.Selector == nil { - return false - } - - v := reflect.ValueOf(f.Selector).Elem() - t := reflect.TypeOf(f.Selector).Elem() - - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - if field.Kind() != reflect.Ptr { - continue - } - - if field.IsNil() { - continue - } - - fieldname := t.Field(i).Name - value := field.Elem().Interface() - - itemV := reflect.ValueOf(item).Elem() - itemF := itemV.FieldByName(fieldname) - if !itemF.IsValid() { - continue - } - - itemValue := itemF.Elem().Interface() - if itemValue != value { - return false - } - } - - return true -} - -// Get the field for a given functionType -func (f *FilterType) SetDataForFunction(tagType EEBusTagTypeType, fct FunctionType, data any) { - if data == nil || reflect.ValueOf(data).Kind() != reflect.Ptr { - return - } - - v := reflect.ValueOf(*f) - dv := reflect.ValueOf(f).Elem() - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - if field.Kind() != reflect.Ptr { - continue - } - - sf := v.Type().Field(i) - // Exclude the generic fields - if sf.Name == "CmdControl" || sf.Name == "FilterId" { - continue - } - - eebusTags := EEBusTags(sf) - function, exists := eebusTags[EEBusTagFunction] - if !exists { - continue - } - typ, exists := eebusTags[EEBusTagType] - if !exists || len(typ) == 0 { - continue - } - if typ != string(tagType) { - continue - } - - if fct != FunctionType(function) { - continue - } - - n := v.Type().Field(i).Name - ff := dv.FieldByName(n) - - if !ff.CanSet() { - break - } - - if reflect.ValueOf(data).IsNil() { - typ := reflect.TypeOf(data).Elem() - ff.Set(reflect.New(typ)) - return - } - - dataV := reflect.ValueOf(data) - dataC := dataV.Convert(ff.Type()) - ff.Set(dataC) - return - } -} - -// Get the data and some meta data for the current value -func (f *FilterType) Data() (*FilterData, error) { - var elements any = nil - var selector any = nil - var function string - - v := reflect.ValueOf(*f) - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - if f.Kind() != reflect.Ptr { - continue - } - - if f.IsNil() { - continue - } - - sf := v.Type().Field(i) - // Exclude the generic fields - if sf.Name == "CmdControl" || sf.Name == "FilterId" { - continue - } - - eebusTags := EEBusTags(sf) - fname, exists := eebusTags[EEBusTagFunction] - if !exists || len(fname) == 0 { - continue - } - typ, exists := eebusTags[EEBusTagType] - if !exists || len(typ) == 0 { - continue - } - - function = fname - - switch typ { - case string(EEBusTagTypeTypeSelector): - selector = f.Interface() - case string(EEbusTagTypeTypeElements): - elements = f.Interface() - } - } - - if len(function) == 0 { - return nil, errors.New("Data not found in Filter") - } - - ft := util.Ptr(FunctionType(function)) - - return &FilterData{ - Elements: elements, - Selector: selector, - Function: ft, - }, nil -} - -// CmdData stores the function field name for a cmd field -type CmdData struct { - FieldName string - Function *FunctionType - Value any -} - -// Get the field for a given functionType -func (cmd *CmdType) SetDataForFunction(fct FunctionType, data any) { - if data == nil || reflect.ValueOf(data).Kind() != reflect.Ptr { - return - } - - v := reflect.ValueOf(*cmd) - dv := reflect.ValueOf(cmd).Elem() - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - if f.Kind() != reflect.Ptr { - continue - } - - sf := v.Type().Field(i) - // Exclude the CmdOptionGroup fields - if sf.Name == "Function" || sf.Name == "Filter" { - continue - } - - eebusTags := EEBusTags(sf) - function, exists := eebusTags[EEBusTagFunction] - if !exists { - continue - } - - if fct != FunctionType(function) { - continue - } - - n := v.Type().Field(i).Name - ff := dv.FieldByName(n) - - if !ff.CanSet() { - break - } - - if reflect.ValueOf(data).IsNil() { - typ := reflect.TypeOf(data).Elem() - ff.Set(reflect.New(typ)) - return - } - - dataV := reflect.ValueOf(data) - dataC := dataV.Convert(ff.Type()) - ff.Set(dataC) - return - } -} - -// Get the data and some meta data of the current value -func (cmd *CmdType) Data() (*CmdData, error) { - v := reflect.ValueOf(*cmd) - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - if f.Kind() != reflect.Ptr { - continue - } - - if f.IsNil() { - continue - } - - sf := v.Type().Field(i) - // Exclude the CmdOptionGroup fields - if sf.Name == "Function" || sf.Name == "Filter" { - continue - } - - eebusTags := EEBusTags(sf) - function, exists := eebusTags[EEBusTagFunction] - if !exists { - continue - } - - var ft *FunctionType = nil - if len(function) > 0 { - ft = util.Ptr(FunctionType(function)) - } - - return &CmdData{ - FieldName: sf.Name, - Function: ft, - Value: f.Interface(), - }, nil - } - - return nil, errors.New("Data not found in Cmd") -} - -// Get the non empty field name of the data type -func (cmd *CmdType) DataName() string { - data, err := cmd.Data() - if err != nil { - return "unknown" - } - - return data.FieldName -} - -func (cmd *CmdType) ExtractFilter() (filterPartial *FilterType, filterDelete *FilterType) { - if cmd != nil && cmd.Filter != nil && len(cmd.Filter) > 0 { - for i := range cmd.Filter { - if cmd.Filter[i].CmdControl.Partial != nil { - filterPartial = &cmd.Filter[i] - } else if cmd.Filter[i].CmdControl.Delete != nil { - filterDelete = &cmd.Filter[i] - } - } - } - return -} - -func NewFilterTypePartial() *FilterType { - return &FilterType{CmdControl: &CmdControlType{Partial: &ElementTagType{}}} -} diff --git a/spine/model/commandframe_additions_test.go b/spine/model/commandframe_additions_test.go deleted file mode 100644 index 0a1e97d6..00000000 --- a/spine/model/commandframe_additions_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestFilterType_Selector_Data(t *testing.T) { - data := &ElectricalConnectionDescriptionListDataSelectorsType{ - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), - ScopeType: util.Ptr(ScopeTypeTypeACPower), - } - - sut := &FilterType{ - ElectricalConnectionDescriptionListDataSelectors: data, - } - - // Act - cmdData, err := sut.Data() - assert.Nil(t, err) - assert.NotNil(t, cmdData) - assert.Equal(t, FunctionTypeElectricalConnectionDescriptionListData, *cmdData.Function) - assert.Equal(t, data, cmdData.Selector) -} - -func TestFilterType_Selector_SetDataForFunction(t *testing.T) { - cmd := FilterType{} - cmd.SetDataForFunction(EEBusTagTypeTypeSelector, FunctionTypeElectricalConnectionDescriptionListData, &ElectricalConnectionDescriptionListDataSelectorsType{}) - assert.NotNil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) - - cmd = FilterType{} - cmd.SetDataForFunction(EEBusTagTypeTypeSelector, FunctionTypeElectricalConnectionDescriptionListData, nil) - assert.Nil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) - - var test *ElectricalConnectionDescriptionListDataSelectorsType - cmd = FilterType{} - cmd.SetDataForFunction(EEBusTagTypeTypeSelector, FunctionTypeElectricalConnectionDescriptionListData, test) - assert.NotNil(t, cmd.ElectricalConnectionDescriptionListDataSelectors) -} - -func TestFilterType_Elements_Data(t *testing.T) { - data := &ElectricalConnectionDescriptionDataElementsType{ - ElectricalConnectionId: util.Ptr(ElementTagType{}), - } - - sut := &FilterType{ - ElectricalConnectionDescriptionDataElements: data, - } - - // Act - cmdData, err := sut.Data() - assert.Nil(t, err) - assert.NotNil(t, cmdData) - assert.Equal(t, FunctionTypeElectricalConnectionDescriptionListData, *cmdData.Function) - assert.Equal(t, data, cmdData.Elements) -} - -func TestFilterType_Elements_SetDataForFunction(t *testing.T) { - cmd := FilterType{} - cmd.SetDataForFunction(EEbusTagTypeTypeElements, FunctionTypeElectricalConnectionDescriptionListData, &ElectricalConnectionDescriptionDataElementsType{}) - assert.NotNil(t, cmd.ElectricalConnectionDescriptionDataElements) - - cmd = FilterType{} - cmd.SetDataForFunction(EEbusTagTypeTypeElements, FunctionTypeElectricalConnectionDescriptionListData, nil) - assert.Nil(t, cmd.ElectricalConnectionDescriptionDataElements) - - var test *ElectricalConnectionDescriptionDataElementsType - cmd = FilterType{} - cmd.SetDataForFunction(EEbusTagTypeTypeElements, FunctionTypeElectricalConnectionDescriptionListData, test) - assert.NotNil(t, cmd.ElectricalConnectionDescriptionDataElements) -} - -func TestCmdType_Data(t *testing.T) { - data := &NodeManagementDetailedDiscoveryDataType{ - SpecificationVersionList: &NodeManagementSpecificationVersionListType{[]SpecificationVersionDataType{SpecificationVersionDataType("dummy")}}, - } - - sut := &CmdType{ - NodeManagementDetailedDiscoveryData: data, - } - - // Act - cmdData, err := sut.Data() - assert.Nil(t, err) - assert.NotNil(t, cmdData) - assert.Equal(t, "NodeManagementDetailedDiscoveryData", cmdData.FieldName) - assert.Equal(t, FunctionTypeNodeManagementDetailedDiscoveryData, *cmdData.Function) - assert.Equal(t, data, cmdData.Value) -} - -func TestCmdType_SetDataForFunction(t *testing.T) { - cmd := CmdType{} - cmd.SetDataForFunction(FunctionTypeElectricalConnectionDescriptionListData, &ElectricalConnectionDescriptionListDataType{}) - assert.NotNil(t, cmd.ElectricalConnectionDescriptionListData) - - cmd = CmdType{} - cmd.SetDataForFunction(FunctionTypeElectricalConnectionDescriptionListData, nil) - assert.Nil(t, cmd.ElectricalConnectionDescriptionListData) - - var test *ElectricalConnectionDescriptionListDataType - cmd = CmdType{} - cmd.SetDataForFunction(FunctionTypeElectricalConnectionDescriptionListData, test) - assert.NotNil(t, cmd.ElectricalConnectionDescriptionListData) -} - -func TestCmdType_ExtractFilter_NoFilter(t *testing.T) { - sut := &CmdType{ - NodeManagementDetailedDiscoveryData: &NodeManagementDetailedDiscoveryDataType{}, - } - - // Act - filterPartial, filterDelete := sut.ExtractFilter() - assert.Nil(t, filterPartial) - assert.Nil(t, filterDelete) -} - -func TestCmdType_ExtractFilter_FilterPartialDelete(t *testing.T) { - - filterP := FilterType{ - CmdControl: &CmdControlType{Partial: &ElementTagType{}}, - NodeManagementDetailedDiscoveryDataSelectors: &NodeManagementDetailedDiscoveryDataSelectorsType{}, - } - filterD := FilterType{ - CmdControl: &CmdControlType{Delete: &ElementTagType{}}, - NodeManagementDetailedDiscoveryDataSelectors: &NodeManagementDetailedDiscoveryDataSelectorsType{}, - } - - sut := &CmdType{ - Filter: []FilterType{filterD, filterP}, - NodeManagementDetailedDiscoveryData: &NodeManagementDetailedDiscoveryDataType{}, - } - - // Act - filterPartial, filterDelete := sut.ExtractFilter() - assert.NotNil(t, filterPartial) - assert.Equal(t, &filterP, filterPartial) - assert.NotNil(t, filterDelete) - assert.Equal(t, &filterD, filterDelete) -} diff --git a/spine/model/commondatatypes.go b/spine/model/commondatatypes.go deleted file mode 100644 index f7d6c958..00000000 --- a/spine/model/commondatatypes.go +++ /dev/null @@ -1,988 +0,0 @@ -package model - -type ElementTagType struct{} - -type LabelType string - -type DescriptionType string - -type SpecificationVersionType string - -type TimePeriodType struct { - StartTime *AbsoluteOrRelativeTimeType `json:"startTime,omitempty"` - EndTime *AbsoluteOrRelativeTimeType `json:"endTime,omitempty"` -} - -type TimePeriodElementsType struct { - StartTime *ElementTagType `json:"startTime,omitempty"` - EndTime *ElementTagType `json:"endTime,omitempty"` -} - -type TimestampIntervalType struct { - StartTime *AbsoluteOrRelativeTimeType `json:"startTime,omitempty"` - EndTime *AbsoluteOrRelativeTimeType `json:"endTime,omitempty"` -} - -type TimeType string - -type DateType string - -type DateTimeType string - -type DurationType string - -type AbsoluteOrRelativeTimeType string - -type RecurringIntervalType string - -const ( - RecurringIntervalTypeYearly RecurringIntervalType = "yearly" - RecurringIntervalTypeMonthly RecurringIntervalType = "monthly" - RecurringIntervalTypeWeekly RecurringIntervalType = "weekly" - RecurringIntervalTypeDaily RecurringIntervalType = "daily" - RecurringIntervalTypeHourly RecurringIntervalType = "hourly" - RecurringIntervalTypeEveryminute RecurringIntervalType = "everyMinute" - RecurringIntervalTypeEverysecond RecurringIntervalType = "everySecond" -) - -type MonthType string - -const ( - MonthTypeJanuary MonthType = "january" - MonthTypeFebruary MonthType = "february" - MonthTypeMarch MonthType = "march" - MonthTypeApril MonthType = "april" - MonthTypeMay MonthType = "may" - MonthTypeJune MonthType = "june" - MonthTypeJuly MonthType = "july" - MonthTypeAugust MonthType = "august" - MonthTypeSeptember MonthType = "september" - MonthTypeOctober MonthType = "october" - MonthTypeNovember MonthType = "november" - MonthTypeDecember MonthType = "december" -) - -type DayOfMonthType uint8 - -type CalendarWeekType uint8 - -type DayOfWeekType string - -const ( - DayOfWeekTypeMonday DayOfWeekType = "monday" - DayOfWeekTypeTuesday DayOfWeekType = "tuesday" - DayOfWeekTypeWednesday DayOfWeekType = "wednesday" - DayOfWeekTypeThursday DayOfWeekType = "thursday" - DayOfWeekTypeFriday DayOfWeekType = "friday" - DayOfWeekTypeSaturday DayOfWeekType = "saturday" - DayOfWeekTypeSunday DayOfWeekType = "sunday" -) - -type DaysOfWeekType struct { - Monday *ElementTagType `json:"monday,omitempty"` - Tuesday *ElementTagType `json:"tuesday,omitempty"` - Wednesday *ElementTagType `json:"wednesday,omitempty"` - Thursday *ElementTagType `json:"thursday,omitempty"` - Friday *ElementTagType `json:"friday,omitempty"` - Saturday *ElementTagType `json:"saturday,omitempty"` - Sunday *ElementTagType `json:"sunday,omitempty"` -} - -type OccurrenceType string - -const ( - OccurrenceTypeFirst OccurrenceType = "first" - OccurrenceTypeSecond OccurrenceType = "second" - OccurrenceTypeThird OccurrenceType = "third" - OccurrenceTypeFourth OccurrenceType = "fourth" - OccurrenceTypeLast OccurrenceType = "last" -) - -type AbsoluteOrRecurringTimeType struct { - DateTime *DateTimeType `json:"dateTime,omitempty"` - Month *MonthType `json:"month,omitempty"` - DayOfMonth *DayOfMonthType `json:"dayOfMonth,omitempty"` - CalendarWeek *CalendarWeekType `json:"calendarWeek,omitempty"` - DayOfWeekOccurrence *OccurrenceType `json:"dayOfWeekOccurrence,omitempty"` - DaysOfWeek *DaysOfWeekType `json:"daysOfWeek,omitempty"` - Time *TimeType `json:"time,omitempty"` - Relative *DurationType `json:"relative,omitempty"` -} - -type AbsoluteOrRecurringTimeElementsType struct { - DateTime *ElementTagType `json:"dateTime,omitempty"` - Month *ElementTagType `json:"month,omitempty"` - DayOfMonth *ElementTagType `json:"dayOfMonth,omitempty"` - CalendarWeek *ElementTagType `json:"calendarWeek,omitempty"` - DayOfWeekOccurrence *ElementTagType `json:"dayOfWeekOccurrence,omitempty"` - DaysOfWeek *ElementTagType `json:"daysOfWeek,omitempty"` - Time *ElementTagType `json:"time,omitempty"` - Relative *ElementTagType `json:"relative,omitempty"` -} - -type RecurrenceInformationType struct { - RecurringInterval *RecurringIntervalType `json:"recurringInterval,omitempty"` - RecurringIntervalStep *uint `json:"recurringIntervalStep,omitempty"` - FirstExecution *DateTimeType `json:"firstExecution,omitempty"` - ExecutionCount *uint `json:"executionCount,omitempty"` - LastExecution *DateTimeType `json:"lastExecution,omitempty"` -} - -type RecurrenceInformationElementsType struct { - RecurringInterval *ElementTagType `json:"recurringInterval,omitempty"` - RecurringIntervalStep *ElementTagType `json:"recurringIntervalStep,omitempty"` - FirstExecution *ElementTagType `json:"firstExecution,omitempty"` - ExecutionCount *ElementTagType `json:"executionCount,omitempty"` - LastExecution *ElementTagType `json:"lastExecution,omitempty"` -} - -type ScaledNumberRangeType struct { - Min *ScaledNumberType `json:"min,omitempty"` - Max *ScaledNumberType `json:"max,omitempty"` -} - -type ScaledNumberRangeElementsType struct { - Min *ElementTagType `json:"min,omitempty"` - Max *ElementTagType `json:"max,omitempty"` -} - -type ScaledNumberSetType struct { - Value []ScaledNumberType `json:"value,omitempty"` - Range []ScaledNumberRangeType `json:"range,omitempty"` -} - -type ScaledNumberSetElementsType struct { - Value *ElementTagType `json:"value,omitempty"` - Range *ElementTagType `json:"range,omitempty"` -} - -type NumberType int64 - -type ScaleType int8 - -type ScaledNumberType struct { - Number *NumberType `json:"number,omitempty"` - Scale *ScaleType `json:"scale,omitempty"` -} - -type ScaledNumberElementsType struct { - Number *ElementTagType `json:"number,omitempty"` - Scale *ElementTagType `json:"scale,omitempty"` -} - -type MaxResponseDelayType DurationType - -type CommodityTypeType string - -const ( - CommodityTypeTypeElectricity CommodityTypeType = "electricity" - CommodityTypeTypeGas CommodityTypeType = "gas" - CommodityTypeTypeOil CommodityTypeType = "oil" - CommodityTypeTypeWater CommodityTypeType = "water" - CommodityTypeTypeWastewater CommodityTypeType = "wasteWater" - CommodityTypeTypeDomestichotwater CommodityTypeType = "domesticHotWater" - CommodityTypeTypeHeatingwater CommodityTypeType = "heatingWater" - CommodityTypeTypeSteam CommodityTypeType = "steam" - CommodityTypeTypeHeat CommodityTypeType = "heat" - CommodityTypeTypeCoolingload CommodityTypeType = "coolingLoad" - CommodityTypeTypeAir CommodityTypeType = "air" -) - -type EnergyDirectionType string - -const ( - EnergyDirectionTypeConsume EnergyDirectionType = "consume" - EnergyDirectionTypeProduce EnergyDirectionType = "produce" -) - -type EnergyModeType string - -const ( - EnergyModeTypeConsume EnergyModeType = "consume" - EnergyModeTypeProduce EnergyModeType = "produce" - EnergyModeTypeIdle EnergyModeType = "idle" - EnergyModeTypeAuto EnergyModeType = "auto" -) - -type UnitOfMeasurementType string - -const ( - UnitOfMeasurementTypeUnknown UnitOfMeasurementType = "unknown" - UnitOfMeasurementType1 UnitOfMeasurementType = "1" - UnitOfMeasurementTypem UnitOfMeasurementType = "m" - UnitOfMeasurementTypekg UnitOfMeasurementType = "kg" - UnitOfMeasurementTypes UnitOfMeasurementType = "s" - UnitOfMeasurementTypeA UnitOfMeasurementType = "A" - UnitOfMeasurementTypeK UnitOfMeasurementType = "K" - UnitOfMeasurementTypemol UnitOfMeasurementType = "mol" - UnitOfMeasurementTypecd UnitOfMeasurementType = "cd" - UnitOfMeasurementTypeV UnitOfMeasurementType = "V" - UnitOfMeasurementTypeW UnitOfMeasurementType = "W" - UnitOfMeasurementTypeWh UnitOfMeasurementType = "Wh" - UnitOfMeasurementTypeVA UnitOfMeasurementType = "VA" - UnitOfMeasurementTypeVAh UnitOfMeasurementType = "VAh" - UnitOfMeasurementTypevar UnitOfMeasurementType = "var" - UnitOfMeasurementTypevarh UnitOfMeasurementType = "varh" - UnitOfMeasurementTypedegC UnitOfMeasurementType = "degC" - UnitOfMeasurementTypedegF UnitOfMeasurementType = "degF" - UnitOfMeasurementTypeLm UnitOfMeasurementType = "Lm" - UnitOfMeasurementTypelx UnitOfMeasurementType = "lx" - UnitOfMeasurementTypeOhm UnitOfMeasurementType = "Ohm" - UnitOfMeasurementTypeHz UnitOfMeasurementType = "Hz" - UnitOfMeasurementTypedB UnitOfMeasurementType = "dB" - UnitOfMeasurementTypedBm UnitOfMeasurementType = "dBm" - UnitOfMeasurementTypepct UnitOfMeasurementType = "pct" - UnitOfMeasurementTypeppm UnitOfMeasurementType = "ppm" - UnitOfMeasurementTypel UnitOfMeasurementType = "l" - UnitOfMeasurementTypels UnitOfMeasurementType = "l/s" - UnitOfMeasurementTypelh UnitOfMeasurementType = "l/h" - UnitOfMeasurementTypedeg UnitOfMeasurementType = "deg" - UnitOfMeasurementTyperad UnitOfMeasurementType = "rad" - UnitOfMeasurementTyperads UnitOfMeasurementType = "rad/s" - UnitOfMeasurementTypesr UnitOfMeasurementType = "sr" - UnitOfMeasurementTypeGy UnitOfMeasurementType = "Gy" - UnitOfMeasurementTypeBq UnitOfMeasurementType = "Bq" - UnitOfMeasurementTypeBqm3 UnitOfMeasurementType = "Bq/m^3" - UnitOfMeasurementTypeSv UnitOfMeasurementType = "Sv" - UnitOfMeasurementTypeRd UnitOfMeasurementType = "Rd" - UnitOfMeasurementTypeC UnitOfMeasurementType = "C" - UnitOfMeasurementTypeF UnitOfMeasurementType = "F" - UnitOfMeasurementTypeH UnitOfMeasurementType = "H" - UnitOfMeasurementTypeJ UnitOfMeasurementType = "J" - UnitOfMeasurementTypeN UnitOfMeasurementType = "N" - UnitOfMeasurementTypeNm UnitOfMeasurementType = "N_m" - UnitOfMeasurementTypeNs UnitOfMeasurementType = "N_s" - UnitOfMeasurementTypeWb UnitOfMeasurementType = "Wb" - UnitOfMeasurementTypeT UnitOfMeasurementType = "T" - UnitOfMeasurementTypePa UnitOfMeasurementType = "Pa" - UnitOfMeasurementTypebar UnitOfMeasurementType = "bar" - UnitOfMeasurementTypeatm UnitOfMeasurementType = "atm" - UnitOfMeasurementTypepsi UnitOfMeasurementType = "psi" - UnitOfMeasurementTypemmHg UnitOfMeasurementType = "mmHg" - UnitOfMeasurementTypem2 UnitOfMeasurementType = "m^2" - UnitOfMeasurementTypem3 UnitOfMeasurementType = "m^3" - UnitOfMeasurementTypem3h UnitOfMeasurementType = "m^3/h" - UnitOfMeasurementTypems UnitOfMeasurementType = "m/s" - UnitOfMeasurementTypems2 UnitOfMeasurementType = "m/s^2" - UnitOfMeasurementTypem3s UnitOfMeasurementType = "m^3/s" - UnitOfMeasurementTypemm3 UnitOfMeasurementType = "m/m^3" - UnitOfMeasurementTypekgm3 UnitOfMeasurementType = "kg/m^3" - UnitOfMeasurementTypekgm UnitOfMeasurementType = "kg_m" - UnitOfMeasurementTypem2s UnitOfMeasurementType = "m^2/s" - UnitOfMeasurementTypewmk UnitOfMeasurementType = "W/m_K" - UnitOfMeasurementTypeJK UnitOfMeasurementType = "J/K" - UnitOfMeasurementType1s UnitOfMeasurementType = "1/s" - UnitOfMeasurementTypeWm2 UnitOfMeasurementType = "W/m^2" - UnitOfMeasurementTypeJm2 UnitOfMeasurementType = "J/m^2" - UnitOfMeasurementTypeS UnitOfMeasurementType = "S" - UnitOfMeasurementTypeSm UnitOfMeasurementType = "S/m" - UnitOfMeasurementTypeKs UnitOfMeasurementType = "K/s" - UnitOfMeasurementTypePas UnitOfMeasurementType = "Pa/s" - UnitOfMeasurementTypeJkgK UnitOfMeasurementType = "J/kg_K" - UnitOfMeasurementTypeVs UnitOfMeasurementType = "Vs" - UnitOfMeasurementTypeVm UnitOfMeasurementType = "V/m" - UnitOfMeasurementTypeVHz UnitOfMeasurementType = "V/Hz" - UnitOfMeasurementTypeAs UnitOfMeasurementType = "As" - UnitOfMeasurementTypeAm UnitOfMeasurementType = "A/m" - UnitOfMeasurementTypeHzs UnitOfMeasurementType = "Hz/s" - UnitOfMeasurementTypekgs UnitOfMeasurementType = "kg/s" - UnitOfMeasurementTypekgm2 UnitOfMeasurementType = "kg_m^2" - UnitOfMeasurementTypeJWh UnitOfMeasurementType = "J/Wh" - UnitOfMeasurementTypeWs UnitOfMeasurementType = "W/s" - UnitOfMeasurementTypeft3 UnitOfMeasurementType = "ft^3" - UnitOfMeasurementTypeft3h UnitOfMeasurementType = "ft^3/h" - UnitOfMeasurementTypeccf UnitOfMeasurementType = "ccf" - UnitOfMeasurementTypeccfh UnitOfMeasurementType = "ccf/h" - UnitOfMeasurementTypeUSliqgal UnitOfMeasurementType = "US.liq.gal" - UnitOfMeasurementTypeUSliqgalh UnitOfMeasurementType = "US.liq.gal/h" - UnitOfMeasurementTypeImpgal UnitOfMeasurementType = "Imp.gal" - UnitOfMeasurementTypeImpgalh UnitOfMeasurementType = "Imp.gal/h" - UnitOfMeasurementTypeBtu UnitOfMeasurementType = "Btu" - UnitOfMeasurementTypeBtuh UnitOfMeasurementType = "Btu/h" - UnitOfMeasurementTypeAh UnitOfMeasurementType = "Ah" - UnitOfMeasurementTypekgWh UnitOfMeasurementType = "kg/Wh" -) - -type CurrencyType string - -const ( - CurrencyTypeAed CurrencyType = "AED" - CurrencyTypeAfn CurrencyType = "AFN" - CurrencyTypeAll CurrencyType = "ALL" - CurrencyTypeAmd CurrencyType = "AMD" - CurrencyTypeAng CurrencyType = "ANG" - CurrencyTypeAoa CurrencyType = "AOA" - CurrencyTypeArs CurrencyType = "ARS" - CurrencyTypeAud CurrencyType = "AUD" - CurrencyTypeAwg CurrencyType = "AWG" - CurrencyTypeAzn CurrencyType = "AZN" - CurrencyTypeBam CurrencyType = "BAM" - CurrencyTypeBbd CurrencyType = "BBD" - CurrencyTypeBdt CurrencyType = "BDT" - CurrencyTypeBgn CurrencyType = "BGN" - CurrencyTypeBhd CurrencyType = "BHD" - CurrencyTypeBif CurrencyType = "BIF" - CurrencyTypeBmd CurrencyType = "BMD" - CurrencyTypeBnd CurrencyType = "BND" - CurrencyTypeBob CurrencyType = "BOB" - CurrencyTypeBov CurrencyType = "BOV" - CurrencyTypeBrl CurrencyType = "BRL" - CurrencyTypeBsd CurrencyType = "BSD" - CurrencyTypeBtn CurrencyType = "BTN" - CurrencyTypeBwp CurrencyType = "BWP" - CurrencyTypeByr CurrencyType = "BYR" - CurrencyTypeBzd CurrencyType = "BZD" - CurrencyTypeCad CurrencyType = "CAD" - CurrencyTypeCdf CurrencyType = "CDF" - CurrencyTypeChe CurrencyType = "CHE" - CurrencyTypeChf CurrencyType = "CHF" - CurrencyTypeChw CurrencyType = "CHW" - CurrencyTypeClf CurrencyType = "CLF" - CurrencyTypeClp CurrencyType = "CLP" - CurrencyTypeCny CurrencyType = "CNY" - CurrencyTypeCop CurrencyType = "COP" - CurrencyTypeCou CurrencyType = "COU" - CurrencyTypeCrc CurrencyType = "CRC" - CurrencyTypeCuc CurrencyType = "CUC" - CurrencyTypeCup CurrencyType = "CUP" - CurrencyTypeCve CurrencyType = "CVE" - CurrencyTypeCzk CurrencyType = "CZK" - CurrencyTypeDjf CurrencyType = "DJF" - CurrencyTypeDkk CurrencyType = "DKK" - CurrencyTypeDop CurrencyType = "DOP" - CurrencyTypeDzd CurrencyType = "DZD" - CurrencyTypeEgp CurrencyType = "EGP" - CurrencyTypeErn CurrencyType = "ERN" - CurrencyTypeEtb CurrencyType = "ETB" - CurrencyTypeEur CurrencyType = "EUR" - CurrencyTypeFjd CurrencyType = "FJD" - CurrencyTypeFkp CurrencyType = "FKP" - CurrencyTypeGbp CurrencyType = "GBP" - CurrencyTypeGel CurrencyType = "GEL" - CurrencyTypeGhs CurrencyType = "GHS" - CurrencyTypeGip CurrencyType = "GIP" - CurrencyTypeGmd CurrencyType = "GMD" - CurrencyTypeGnf CurrencyType = "GNF" - CurrencyTypeGtq CurrencyType = "GTQ" - CurrencyTypeGyd CurrencyType = "GYD" - CurrencyTypeHkd CurrencyType = "HKD" - CurrencyTypeHnl CurrencyType = "HNL" - CurrencyTypeHrk CurrencyType = "HRK" - CurrencyTypeHtg CurrencyType = "HTG" - CurrencyTypeHuf CurrencyType = "HUF" - CurrencyTypeIdr CurrencyType = "IDR" - CurrencyTypeIls CurrencyType = "ILS" - CurrencyTypeInr CurrencyType = "INR" - CurrencyTypeIqd CurrencyType = "IQD" - CurrencyTypeIrr CurrencyType = "IRR" - CurrencyTypeIsk CurrencyType = "ISK" - CurrencyTypeJmd CurrencyType = "JMD" - CurrencyTypeJod CurrencyType = "JOD" - CurrencyTypeJpy CurrencyType = "JPY" - CurrencyTypeKes CurrencyType = "KES" - CurrencyTypeKgs CurrencyType = "KGS" - CurrencyTypeKhr CurrencyType = "KHR" - CurrencyTypeKmf CurrencyType = "KMF" - CurrencyTypeKpw CurrencyType = "KPW" - CurrencyTypeKrw CurrencyType = "KRW" - CurrencyTypeKwd CurrencyType = "KWD" - CurrencyTypeKyd CurrencyType = "KYD" - CurrencyTypeKzt CurrencyType = "KZT" - CurrencyTypeLak CurrencyType = "LAK" - CurrencyTypeLbp CurrencyType = "LBP" - CurrencyTypeLkr CurrencyType = "LKR" - CurrencyTypeLrd CurrencyType = "LRD" - CurrencyTypeLsl CurrencyType = "LSL" - CurrencyTypeLyd CurrencyType = "LYD" - CurrencyTypeMad CurrencyType = "MAD" - CurrencyTypeMdl CurrencyType = "MDL" - CurrencyTypeMga CurrencyType = "MGA" - CurrencyTypeMkd CurrencyType = "MKD" - CurrencyTypeMmk CurrencyType = "MMK" - CurrencyTypeMnt CurrencyType = "MNT" - CurrencyTypeMop CurrencyType = "MOP" - CurrencyTypeMro CurrencyType = "MRO" - CurrencyTypeMur CurrencyType = "MUR" - CurrencyTypeMvr CurrencyType = "MVR" - CurrencyTypeMwk CurrencyType = "MWK" - CurrencyTypeMxn CurrencyType = "MXN" - CurrencyTypeMxv CurrencyType = "MXV" - CurrencyTypeMyr CurrencyType = "MYR" - CurrencyTypeMzn CurrencyType = "MZN" - CurrencyTypeNad CurrencyType = "NAD" - CurrencyTypeNgn CurrencyType = "NGN" - CurrencyTypeNio CurrencyType = "NIO" - CurrencyTypeNok CurrencyType = "NOK" - CurrencyTypeNpr CurrencyType = "NPR" - CurrencyTypeNzd CurrencyType = "NZD" - CurrencyTypeOmr CurrencyType = "OMR" - CurrencyTypePab CurrencyType = "PAB" - CurrencyTypePen CurrencyType = "PEN" - CurrencyTypePgk CurrencyType = "PGK" - CurrencyTypePhp CurrencyType = "PHP" - CurrencyTypePkr CurrencyType = "PKR" - CurrencyTypePln CurrencyType = "PLN" - CurrencyTypePyg CurrencyType = "PYG" - CurrencyTypeQar CurrencyType = "QAR" - CurrencyTypeRon CurrencyType = "RON" - CurrencyTypeRsd CurrencyType = "RSD" - CurrencyTypeRub CurrencyType = "RUB" - CurrencyTypeRwf CurrencyType = "RWF" - CurrencyTypeSar CurrencyType = "SAR" - CurrencyTypeSbd CurrencyType = "SBD" - CurrencyTypeScr CurrencyType = "SCR" - CurrencyTypeSdg CurrencyType = "SDG" - CurrencyTypeSek CurrencyType = "SEK" - CurrencyTypeSgd CurrencyType = "SGD" - CurrencyTypeShp CurrencyType = "SHP" - CurrencyTypeSll CurrencyType = "SLL" - CurrencyTypeSos CurrencyType = "SOS" - CurrencyTypeSrd CurrencyType = "SRD" - CurrencyTypeSsp CurrencyType = "SSP" - CurrencyTypeStd CurrencyType = "STD" - CurrencyTypeSvc CurrencyType = "SVC" - CurrencyTypeSyp CurrencyType = "SYP" - CurrencyTypeSzl CurrencyType = "SZL" - CurrencyTypeThb CurrencyType = "THB" - CurrencyTypeTjs CurrencyType = "TJS" - CurrencyTypeTmt CurrencyType = "TMT" - CurrencyTypeTnd CurrencyType = "TND" - CurrencyTypeTop CurrencyType = "TOP" - CurrencyTypeTry CurrencyType = "TRY" - CurrencyTypeTtd CurrencyType = "TTD" - CurrencyTypeTwd CurrencyType = "TWD" - CurrencyTypeTzs CurrencyType = "TZS" - CurrencyTypeUah CurrencyType = "UAH" - CurrencyTypeUgx CurrencyType = "UGX" - CurrencyTypeUsd CurrencyType = "USD" - CurrencyTypeUsn CurrencyType = "USN" - CurrencyTypeUyi CurrencyType = "UYI" - CurrencyTypeUyu CurrencyType = "UYU" - CurrencyTypeUzs CurrencyType = "UZS" - CurrencyTypeVef CurrencyType = "VEF" - CurrencyTypeVnd CurrencyType = "VND" - CurrencyTypeVuv CurrencyType = "VUV" - CurrencyTypeWst CurrencyType = "WST" - CurrencyTypeXaf CurrencyType = "XAF" - CurrencyTypeXag CurrencyType = "XAG" - CurrencyTypeXau CurrencyType = "XAU" - CurrencyTypeXba CurrencyType = "XBA" - CurrencyTypeXbb CurrencyType = "XBB" - CurrencyTypeXbc CurrencyType = "XBC" - CurrencyTypeXbd CurrencyType = "XBD" - CurrencyTypeXcd CurrencyType = "XCD" - CurrencyTypeXdr CurrencyType = "XDR" - CurrencyTypeXof CurrencyType = "XOF" - CurrencyTypeXpd CurrencyType = "XPD" - CurrencyTypeXpf CurrencyType = "XPF" - CurrencyTypeXpt CurrencyType = "XPT" - CurrencyTypeXsu CurrencyType = "XSU" - CurrencyTypeXts CurrencyType = "XTS" - CurrencyTypeXua CurrencyType = "XUA" - CurrencyTypeXxx CurrencyType = "XXX" - CurrencyTypeYer CurrencyType = "YER" - CurrencyTypeZar CurrencyType = "ZAR" - CurrencyTypeZmw CurrencyType = "ZMW" - CurrencyTypeZwl CurrencyType = "ZWL" -) - -type AddressDeviceType string - -type AddressEntityType uint - -type AddressFeatureType uint - -type DeviceAddressType struct { - Device *AddressDeviceType `json:"device,omitempty"` -} - -type DeviceAddressElementsType struct { - Device *ElementTagType `json:"device,omitempty"` -} - -type EntityAddressType struct { - Device *AddressDeviceType `json:"device,omitempty"` - Entity []AddressEntityType `json:"entity,omitempty"` -} - -type EntityAddressElementsType struct { - Device *ElementTagType `json:"device,omitempty"` - Entity *ElementTagType `json:"entity,omitempty"` -} - -type FeatureAddressType struct { - Device *AddressDeviceType `json:"device,omitempty"` - Entity []AddressEntityType `json:"entity,omitempty"` - Feature *AddressFeatureType `json:"feature,omitempty"` -} - -type FeatureAddressElementsType struct { - Device *ElementTagType `json:"device,omitempty"` - Entity *ElementTagType `json:"entity,omitempty"` - Feature *ElementTagType `json:"feature,omitempty"` -} - -type ScopeTypeType string - -const ( - ScopeTypeTypeAC ScopeTypeType = "ac" - ScopeTypeTypeACCosPhiGrid ScopeTypeType = "acCosPhiGrid" - ScopeTypeTypeACCurrentA ScopeTypeType = "acCurrentA" - ScopeTypeTypeACCurrentB ScopeTypeType = "acCurrentB" - ScopeTypeTypeACCurrentC ScopeTypeType = "acCurrentC" - ScopeTypeTypeACFrequency ScopeTypeType = "acFrequency" - ScopeTypeTypeACFrequencyGrid ScopeTypeType = "acFrequencyGrid" - ScopeTypeTypeACPowerA ScopeTypeType = "acPowerA" - ScopeTypeTypeACPowerB ScopeTypeType = "acPowerB" - ScopeTypeTypeACPowerC ScopeTypeType = "acPowerC" - ScopeTypeTypeACPowerLimitPct ScopeTypeType = "acPowerLimitPct" - ScopeTypeTypeACPowerTotal ScopeTypeType = "acPowerTotal" - ScopeTypeTypeACVoltageA ScopeTypeType = "acVoltageA" - ScopeTypeTypeACVoltageB ScopeTypeType = "acVoltageB" - ScopeTypeTypeACVoltageC ScopeTypeType = "acVoltageC" - ScopeTypeTypeACYieldDay ScopeTypeType = "acYieldDay" - ScopeTypeTypeACYieldTotal ScopeTypeType = "acYieldTotal" - ScopeTypeTypeDCCurrent ScopeTypeType = "dcCurrent" - ScopeTypeTypeDCPower ScopeTypeType = "dcPower" - ScopeTypeTypeDCString1 ScopeTypeType = "dcString1" - ScopeTypeTypeDCString2 ScopeTypeType = "dcString2" - ScopeTypeTypeDCString3 ScopeTypeType = "dcString3" - ScopeTypeTypeDCString4 ScopeTypeType = "dcString4" - ScopeTypeTypeDCString5 ScopeTypeType = "dcString5" - ScopeTypeTypeDCString6 ScopeTypeType = "dcString6" - ScopeTypeTypeDCTotal ScopeTypeType = "dcTotal" - ScopeTypeTypeDCVoltage ScopeTypeType = "dcVoltage" - ScopeTypeTypeDhwTemperature ScopeTypeType = "dhwTemperature" - ScopeTypeTypeFlowTemperature ScopeTypeType = "flowTemperature" - ScopeTypeTypeOutsideAirTemperature ScopeTypeType = "outsideAirTemperature" - ScopeTypeTypeReturnTemperature ScopeTypeType = "returnTemperature" - ScopeTypeTypeRoomAirTemperature ScopeTypeType = "roomAirTemperature" - ScopeTypeTypeCharge ScopeTypeType = "charge" - ScopeTypeTypeStateOfCharge ScopeTypeType = "stateOfCharge" - ScopeTypeTypeDischarge ScopeTypeType = "discharge" - ScopeTypeTypeGridConsumption ScopeTypeType = "gridConsumption" - ScopeTypeTypeGridFeedIn ScopeTypeType = "gridFeedIn" - ScopeTypeTypeSelfConsumption ScopeTypeType = "selfConsumption" - ScopeTypeTypeOverloadProtection ScopeTypeType = "overloadProtection" - ScopeTypeTypeACPower ScopeTypeType = "acPower" - ScopeTypeTypeACEnergy ScopeTypeType = "acEnergy" - ScopeTypeTypeACCurrent ScopeTypeType = "acCurrent" - ScopeTypeTypeACVoltage ScopeTypeType = "acVoltage" - ScopeTypeTypeBatteryControl ScopeTypeType = "batteryControl" - ScopeTypeTypeSimpleIncentiveTable ScopeTypeType = "simpleIncentiveTable" - ScopeTypeTypeStateOfHealth ScopeTypeType = "stateOfHealth" - ScopeTypeTypeTravelRange ScopeTypeType = "travelRange" - ScopeTypeTypeNominalEnergyCapacity ScopeTypeType = "nominalEnergyCapacity" - ScopeTypeTypeAcPowerReal ScopeTypeType = "acPowerReal" - ScopeTypeTypeAcPowerApparent ScopeTypeType = "acPowerApparent" - ScopeTypeTypeAcPowerReactive ScopeTypeType = "acPowerReactive" - ScopeTypeTypeAcYieldMonth ScopeTypeType = "acYieldMonth" - ScopeTypeTypeAcYieldYear ScopeTypeType = "acYieldYear" - ScopeTypeTypeAcFrequency ScopeTypeType = "acFrequency" - ScopeTypeTypeAcCosPhi ScopeTypeType = "acCosPhi" - ScopeTypeTypeDcEnergy ScopeTypeType = "dcEnergy" - ScopeTypeTypeInsulationResistance ScopeTypeType = "insulationResistance" - ScopeTypeTypeStateOfEnergy ScopeTypeType = "stateOfEnergy" - ScopeTypeTypeUseableCapacity ScopeTypeType = "useableCapacity" - ScopeTypeTypeDcChargeEnergy ScopeTypeType = "dcChargeEnergy" - ScopeTypeTypeDcDischargeEnergy ScopeTypeType = "dcDischargeEnergy" - ScopeTypeTypeLoadCycleCount ScopeTypeType = "loadCycleCount" - ScopeTypeTypeComponentTemperature ScopeTypeType = "componentTemperature" - ScopeTypeTypeGridLimit ScopeTypeType = "gridLimit" - ScopeTypeTypeGridLimitFallback ScopeTypeType = "gridLimitFallback" - ScopeTypeTypeAcPowerApparentTotal ScopeTypeType = "acPowerApparentTotal" - ScopeTypeTypeAcPowerReactiveTotal ScopeTypeType = "acPowerReactiveTotal" - ScopeTypeTypeAcCurrentTotal ScopeTypeType = "acCurrentTotal" - ScopeTypeTypeAcEnergyConsumed ScopeTypeType = "acEnergyConsumed" - ScopeTypeTypeAcEnergyProduced ScopeTypeType = "acEnergyProduced" - ScopeTypeTypeBatteryAcPower ScopeTypeType = "batteryAcPower" - ScopeTypeTypeBatteryAcPowerPhaseSpecific ScopeTypeType = "batteryAcPowerPhaseSpecific" - ScopeTypeTypeBatteryDcPower ScopeTypeType = "batteryDcPower" - ScopeTypeTypePccPower ScopeTypeType = "pccPower" - ScopeTypeTypeActivePowerLimit ScopeTypeType = "activePowerLimit" - ScopeTypeTypeActivePowerLimitPercentage ScopeTypeType = "activePowerLimitPercentage" - ScopeTypeTypeSimpleCommittedIncentiveTable ScopeTypeType = "simpleCommittedIncentiveTable" - ScopeTypeTypeSimplePreliminaryIncentiveTable ScopeTypeType = "simplePreliminaryIncentiveTable" - ScopeTypeTypeCommittedPowerPlan ScopeTypeType = "committedPowerPlan" - ScopeTypeTypePreliminaryPowerPlan ScopeTypeType = "preliminaryPowerPlan" - ScopeTypeTypeIncentiveTableEnConsWithPoETF ScopeTypeType = "incentiveTableEnConsWithPoETF" - ScopeTypeTypeIncentiveTableEnProdWithPoETF ScopeTypeType = "incentiveTableEnProdWithPoETF" - ScopeTypeTypeIncentiveTableEnConsWithPoE ScopeTypeType = "incentiveTableEnConsWithPoE" - ScopeTypeTypeIncentiveTableEnProdWithPoE ScopeTypeType = "incentiveTableEnProdWithPoE" - ScopeTypeTypeIncentiveTableEnConsWithTF ScopeTypeType = "incentiveTableEnConsWithTF" - ScopeTypeTypeIncentiveTableEnProdWithTF ScopeTypeType = "incentiveTableEnProdWithTF" - ScopeTypeTypeActivePowerForecast ScopeTypeType = "activePowerForecast" -) - -type RoleType string - -const ( - RoleTypeClient RoleType = "client" - RoleTypeServer RoleType = "server" - RoleTypeSpecial RoleType = "special" -) - -type FeatureGroupType string - -type DeviceTypeType string - -const ( - DeviceTypeTypeDishwasher DeviceTypeType = "Dishwasher" - DeviceTypeTypeDryer DeviceTypeType = "Dryer" - DeviceTypeTypeEnvironmentSensor DeviceTypeType = "EnvironmentSensor" - DeviceTypeTypeGeneric DeviceTypeType = "Generic" - DeviceTypeTypeHeatgenerationSystem DeviceTypeType = "HeatGenerationSystem" - DeviceTypeTypeHeatsinkSystem DeviceTypeType = "HeatSinkSystem" - DeviceTypeTypeHeatstorageSystem DeviceTypeType = "HeatStorageSystem" - DeviceTypeTypeHVACController DeviceTypeType = "HVACController" - DeviceTypeTypeSubmeter DeviceTypeType = "SubMeter" - DeviceTypeTypeWasher DeviceTypeType = "Washer" - DeviceTypeTypeElectricitySupplySystem DeviceTypeType = "ElectricitySupplySystem" - DeviceTypeTypeEnergyManagementSystem DeviceTypeType = "EnergyManagementSystem" - DeviceTypeTypeInverter DeviceTypeType = "Inverter" - DeviceTypeTypeChargingStation DeviceTypeType = "ChargingStation" -) - -type EntityTypeType string - -const ( - EntityTypeTypeBattery EntityTypeType = "Battery" - EntityTypeTypeCompressor EntityTypeType = "Compressor" - EntityTypeTypeDeviceInformation EntityTypeType = "DeviceInformation" - EntityTypeTypeDHWCircuit EntityTypeType = "DHWCircuit" - EntityTypeTypeDHWStorage EntityTypeType = "DHWStorage" - EntityTypeTypeDishwasher EntityTypeType = "Dishwasher" - EntityTypeTypeDryer EntityTypeType = "Dryer" - EntityTypeTypeElectricalImmersionheater EntityTypeType = "ElectricalImmersionHeater" - EntityTypeTypeFan EntityTypeType = "Fan" - EntityTypeTypeGasHeatingAppliance EntityTypeType = "GasHeatingAppliance" - EntityTypeTypeGeneric EntityTypeType = "Generic" - EntityTypeTypeHeatingBufferStorage EntityTypeType = "HeatingBufferStorage" - EntityTypeTypeHeatingCircuit EntityTypeType = "HeatingCircuit" - EntityTypeTypeHeatingObject EntityTypeType = "HeatingObject" - EntityTypeTypeHeatingZone EntityTypeType = "HeatingZone" - EntityTypeTypeHeatPumpAppliance EntityTypeType = "HeatPumpAppliance" - EntityTypeTypeHeatSinkCircuit EntityTypeType = "HeatSinkCircuit" - EntityTypeTypeHeatSourceCircuit EntityTypeType = "HeatSourceCircuit" - EntityTypeTypeHeatSourceUnit EntityTypeType = "HeatSourceUnit" - EntityTypeTypeHvacController EntityTypeType = "HVACController" - EntityTypeTypeHvacRoom EntityTypeType = "HVACRoom" - EntityTypeTypeInstantDHWheater EntityTypeType = "InstantDHWHeater" - EntityTypeTypeInverter EntityTypeType = "Inverter" - EntityTypeTypeOilHeatingAppliance EntityTypeType = "OilHeatingAppliance" - EntityTypeTypePump EntityTypeType = "Pump" - EntityTypeTypeRefrigerantCircuit EntityTypeType = "RefrigerantCircuit" - EntityTypeTypeSmartEnergyAppliance EntityTypeType = "SmartEnergyAppliance" - EntityTypeTypeSolarDHWStorage EntityTypeType = "SolarDHWStorage" - EntityTypeTypeSolarThermalCircuit EntityTypeType = "SolarThermalCircuit" - EntityTypeTypeSubMeterElectricity EntityTypeType = "SubMeterElectricity" - EntityTypeTypeTemperatureSensor EntityTypeType = "TemperatureSensor" - EntityTypeTypeWasher EntityTypeType = "Washer" - EntityTypeTypeBatterySystem EntityTypeType = "BatterySystem" - EntityTypeTypeElectricityGenerationSystem EntityTypeType = "ElectricityGenerationSystem" - EntityTypeTypeElectricityStorageSystem EntityTypeType = "ElectricityStorageSystem" - EntityTypeTypeGridConnectionPointOfPremises EntityTypeType = "GridConnectionPointOfPremises" - EntityTypeTypeHousehold EntityTypeType = "Household" - EntityTypeTypePVSystem EntityTypeType = "PVSystem" - EntityTypeTypeEV EntityTypeType = "EV" - EntityTypeTypeEVSE EntityTypeType = "EVSE" - EntityTypeTypeChargingOutlet EntityTypeType = "ChargingOutlet" - EntityTypeTypeCEM EntityTypeType = "CEM" - EntityTypeTypePV EntityTypeType = "PV" - EntityTypeTypePVESHybrid EntityTypeType = "PVESHybrid" - EntityTypeTypeElectricalStorage EntityTypeType = "ElectricalStorage" - EntityTypeTypePVString EntityTypeType = "PVString" - EntityTypeTypeGridGuard EntityTypeType = "GridGuard" - EntityTypeTypeControllableSystem EntityTypeType = "ControllableSystem" -) - -type FeatureTypeType string - -const ( - FeatureTypeTypeActuatorLevel FeatureTypeType = "ActuatorLevel" - FeatureTypeTypeActuatorSwitch FeatureTypeType = "ActuatorSwitch" - FeatureTypeTypeAlarm FeatureTypeType = "Alarm" - FeatureTypeTypeDataTunneling FeatureTypeType = "DataTunneling" - FeatureTypeTypeDeviceClassification FeatureTypeType = "DeviceClassification" - FeatureTypeTypeDeviceDiagnosis FeatureTypeType = "DeviceDiagnosis" - FeatureTypeTypeDirectControl FeatureTypeType = "DirectControl" - FeatureTypeTypeElectricalConnection FeatureTypeType = "ElectricalConnection" - FeatureTypeTypeGeneric FeatureTypeType = "Generic" - FeatureTypeTypeHvac FeatureTypeType = "HVAC" - FeatureTypeTypeLoadControl FeatureTypeType = "LoadControl" - FeatureTypeTypeMeasurement FeatureTypeType = "Measurement" - FeatureTypeTypeMessaging FeatureTypeType = "Messaging" - FeatureTypeTypeNetworkManagement FeatureTypeType = "NetworkManagement" - FeatureTypeTypeNodeManagement FeatureTypeType = "NodeManagement" - FeatureTypeTypeOperatingConstraints FeatureTypeType = "OperatingConstraints" - FeatureTypeTypePowerSequences FeatureTypeType = "PowerSequences" - FeatureTypeTypeSensing FeatureTypeType = "Sensing" - FeatureTypeTypeSetpoint FeatureTypeType = "Setpoint" - FeatureTypeTypeSmartEnergyManagementPs FeatureTypeType = "SmartEnergyManagementPs" - FeatureTypeTypeTaskManagement FeatureTypeType = "TaskManagement" - FeatureTypeTypeThreshold FeatureTypeType = "Threshold" - FeatureTypeTypeTimeInformation FeatureTypeType = "TimeInformation" - FeatureTypeTypeTimeTable FeatureTypeType = "TimeTable" - FeatureTypeTypeDeviceConfiguration FeatureTypeType = "DeviceConfiguration" - FeatureTypeTypeSupplyCondition FeatureTypeType = "SupplyCondition" - FeatureTypeTypeTimeSeries FeatureTypeType = "TimeSeries" - FeatureTypeTypeTariffInformation FeatureTypeType = "TariffInformation" - FeatureTypeTypeIncentiveTable FeatureTypeType = "IncentiveTable" - FeatureTypeTypeBill FeatureTypeType = "Bill" - FeatureTypeTypeIdentification FeatureTypeType = "Identification" - FeatureTypeTypeStateInformation FeatureTypeType = "StateInformation" -) - -type FeatureSpecificUsageType string - -const ( - // FeatureDirectControlSpecificUsageEnumType - FeatureSpecificUsageTypeHistory FeatureSpecificUsageType = "History" - FeatureSpecificUsageTypeRealtime FeatureSpecificUsageType = "RealTime" - - // FeatureHvacSpecificUsageEnumType - FeatureSpecificUsageTypeOperationmode FeatureSpecificUsageType = "OperationMode" - FeatureSpecificUsageTypeOverrun FeatureSpecificUsageType = "Overrun" - - // FeatureMeasurementSpecificUsageEnumType - FeatureSpecificUsageTypeContact FeatureSpecificUsageType = "Contact" - FeatureSpecificUsageTypeElectrical FeatureSpecificUsageType = "Electrical" - FeatureSpecificUsageTypeHeat FeatureSpecificUsageType = "Heat" - FeatureSpecificUsageTypeLevel FeatureSpecificUsageType = "Level" - FeatureSpecificUsageTypePressure FeatureSpecificUsageType = "Pressure" - FeatureSpecificUsageTypeTemperature FeatureSpecificUsageType = "Temperature" - - // FeatureSetpointSpecificUsageEnumType - - // FeatureSmartEnergyManagementPsSpecificUsageEnumType - FeatureSpecificUsageTypeFixedForecast FeatureSpecificUsageType = "FixedForecast" - FeatureSpecificUsageTypeFlexibleChosenForecast FeatureSpecificUsageType = "FlexibleChosenForecast" - FeatureSpecificUsageTypeFlexibleOptionalForecast FeatureSpecificUsageType = "FlexibleOptionalForecast" - FeatureSpecificUsageTypeOptionalSequenceBasedImmediateControl FeatureSpecificUsageType = "OptionalSequenceBasedImmediateControl" -) - -type FeatureDirectControlSpecificUsageEnumType string - -const ( - FeatureDirectControlSpecificUsageEnumTypeHistory FeatureDirectControlSpecificUsageEnumType = "History" - FeatureDirectControlSpecificUsageEnumTypeRealtime FeatureDirectControlSpecificUsageEnumType = "RealTime" -) - -type FeatureHvacSpecificUsageEnumType string - -const ( - FeatureHvacSpecificUsageEnumTypeOperationmode FeatureHvacSpecificUsageEnumType = "OperationMode" - FeatureHvacSpecificUsageEnumTypeOverrun FeatureHvacSpecificUsageEnumType = "Overrun" -) - -type FeatureMeasurementSpecificUsageEnumType string - -const ( - FeatureMeasurementSpecificUsageEnumTypeContact FeatureMeasurementSpecificUsageEnumType = "Contact" - FeatureMeasurementSpecificUsageEnumTypeElectrical FeatureMeasurementSpecificUsageEnumType = "Electrical" - FeatureMeasurementSpecificUsageEnumTypeHeat FeatureMeasurementSpecificUsageEnumType = "Heat" - FeatureMeasurementSpecificUsageEnumTypeLevel FeatureMeasurementSpecificUsageEnumType = "Level" - FeatureMeasurementSpecificUsageEnumTypePressure FeatureMeasurementSpecificUsageEnumType = "Pressure" - FeatureMeasurementSpecificUsageEnumTypeTemperature FeatureMeasurementSpecificUsageEnumType = "Temperature" -) - -type FeatureSetpointSpecificUsageEnumType string - -const ( - // FeatureMeasurementSpecificUsageEnumType - FeatureSetpointSpecificUsageEnumTypeContact FeatureSetpointSpecificUsageEnumType = "Contact" - FeatureSetpointSpecificUsageEnumTypeElectrical FeatureSetpointSpecificUsageEnumType = "Electrical" - FeatureSetpointSpecificUsageEnumTypeHeat FeatureSetpointSpecificUsageEnumType = "Heat" - FeatureSetpointSpecificUsageEnumTypeLevel FeatureSetpointSpecificUsageEnumType = "Level" - FeatureSetpointSpecificUsageEnumTypePressure FeatureSetpointSpecificUsageEnumType = "Pressure" - FeatureSetpointSpecificUsageEnumTypeTemperature FeatureSetpointSpecificUsageEnumType = "Temperature" -) - -type FeatureSmartEnergyManagementPsSpecificUsageEnumType string - -const ( - FeatureSmartEnergyManagementPsSpecificUsageEnumTypeFixedForecast FeatureSmartEnergyManagementPsSpecificUsageEnumType = "FixedForecast" - FeatureSmartEnergyManagementPsSpecificUsageEnumTypeFlexibleChosenForecast FeatureSmartEnergyManagementPsSpecificUsageEnumType = "FlexibleChosenForecast" - FeatureSmartEnergyManagementPsSpecificUsageEnumTypeFlexibleOptionalForecast FeatureSmartEnergyManagementPsSpecificUsageEnumType = "FlexibleOptionalForecast" - FeatureSmartEnergyManagementPsSpecificUsageEnumTypeOptionalSequenceBasedImmediateControl FeatureSmartEnergyManagementPsSpecificUsageEnumType = "OptionalSequenceBasedImmediateControl" -) - -type FunctionType string - -const ( - FunctionTypeActuatorLevelData FunctionType = "actuatorLevelData" - FunctionTypeActuatorLevelDescriptionData FunctionType = "actuatorLevelDescriptionData" - FunctionTypeActuatorSwitchData FunctionType = "actuatorSwitchData" - FunctionTypeActuatorSwitchDescriptionData FunctionType = "actuatorSwitchDescriptionData" - FunctionTypeAlarmListData FunctionType = "alarmListData" - FunctionTypeBindingManagementDeleteCall FunctionType = "bindingManagementDeleteCall" - FunctionTypeBindingManagementEntryListData FunctionType = "bindingManagementEntryListData" - FunctionTypeBindingManagementRequestCall FunctionType = "bindingManagementRequestCall" - FunctionTypeDataTunnelingCall FunctionType = "dataTunnelingCall" - FunctionTypeDeviceClassificationManufacturerData FunctionType = "deviceClassificationManufacturerData" - FunctionTypeDeviceClassificationUserData FunctionType = "deviceClassificationUserData" - FunctionTypeDeviceDiagnosisHeartbeatData FunctionType = "deviceDiagnosisHeartbeatData" - FunctionTypeDeviceDiagnosisServiceData FunctionType = "deviceDiagnosisServiceData" - FunctionTypeDeviceDiagnosisStateData FunctionType = "deviceDiagnosisStateData" - FunctionTypeDirectControlActivityListData FunctionType = "directControlActivityListData" - FunctionTypeDirectControlDescriptionData FunctionType = "directControlDescriptionData" - FunctionTypeElectricalConnectionDescriptionListData FunctionType = "electricalConnectionDescriptionListData" - FunctionTypeElectricalConnectionParameterDescriptionListData FunctionType = "electricalConnectionParameterDescriptionListData" - FunctionTypeElectricalConnectionStateListData FunctionType = "electricalConnectionStateListData" - FunctionTypeHvacOperationModeDescriptionListData FunctionType = "hvacOperationModeDescriptionListData" - FunctionTypeHvacOverrunDescriptionListData FunctionType = "hvacOverrunDescriptionListData" - FunctionTypeHvacOverrunListData FunctionType = "hvacOverrunListData" - FunctionTypeHvacSystemFunctionDescriptionListData FunctionType = "hvacSystemFunctionDescriptionListData" - FunctionTypeHvacSystemFunctionListData FunctionType = "hvacSystemFunctionListData" - FunctionTypeHvacSystemFunctionOperationModeRelationListData FunctionType = "hvacSystemFunctionOperationModeRelationListData" - FunctionTypeHvacSystemFunctionPowerSequenceRelationListData FunctionType = "hvacSystemFunctionPowerSequenceRelationListData" - FunctionTypeHvacSystemFunctionSetPointRelationListData FunctionType = "hvacSystemFunctionSetpointRelationListData" - FunctionTypeLoadControlEventListData FunctionType = "loadControlEventListData" - FunctionTypeLoadControlStateListData FunctionType = "loadControlStateListData" - FunctionTypeMeasurementConstraintsListData FunctionType = "measurementConstraintsListData" - FunctionTypeMeasurementDescriptionListData FunctionType = "measurementDescriptionListData" - FunctionTypeMeasurementListData FunctionType = "measurementListData" - FunctionTypeMeasurementThresholdRelationListData FunctionType = "measurementThresholdRelationListData" - FunctionTypeMessagingListData FunctionType = "messagingListData" - FunctionTypeNetworkManagementAbortCall FunctionType = "networkManagementAbortCall" - FunctionTypeNetworkManagementAddNodeCall FunctionType = "networkManagementAddNodeCall" - FunctionTypeNetworkManagementDeviceDescriptionListData FunctionType = "networkManagementDeviceDescriptionListData" - FunctionTypeNetworkManagementDiscoverCall FunctionType = "networkManagementDiscoverCall" - FunctionTypeNetworkManagementEntityDescriptionListData FunctionType = "networkManagementEntityDescriptionListData" - FunctionTypeNetworkManagementFeatureDescriptionListData FunctionType = "networkManagementFeatureDescriptionListData" - FunctionTypeNetworkManagementJoiningModeData FunctionType = "networkManagementJoiningModeData" - FunctionTypeNetworkManagementModifyNodeCall FunctionType = "networkManagementModifyNodeCall" - FunctionTypeNetworkManagementProcessStateData FunctionType = "networkManagementProcessStateData" - FunctionTypeNetworkManagementRemoveNodeCall FunctionType = "networkManagementRemoveNodeCall" - FunctionTypeNetworkManagementReportCandidateData FunctionType = "networkManagementReportCandidateData" - FunctionTypeNetworkManagementScanNetworkCall FunctionType = "networkManagementScanNetworkCall" - FunctionTypeNodeManagementBindingData FunctionType = "nodeManagementBindingData" - FunctionTypeNodeManagementBindingDeleteCall FunctionType = "nodeManagementBindingDeleteCall" - FunctionTypeNodeManagementBindingRequestCall FunctionType = "nodeManagementBindingRequestCall" - FunctionTypeNodeManagementDestinationListData FunctionType = "nodeManagementDestinationListData" - FunctionTypeNodeManagementDetailedDiscoveryData FunctionType = "nodeManagementDetailedDiscoveryData" - FunctionTypeNodeManagementSubscriptionData FunctionType = "nodeManagementSubscriptionData" - FunctionTypeNodeManagementSubscriptionDeleteCall FunctionType = "nodeManagementSubscriptionDeleteCall" - FunctionTypeNodeManagementSubscriptionRequestCall FunctionType = "nodeManagementSubscriptionRequestCall" - FunctionTypeOperatingConstraintsDurationListData FunctionType = "operatingConstraintsDurationListData" - FunctionTypeOperatingConstraintsInterruptListData FunctionType = "operatingConstraintsInterruptListData" - FunctionTypeOperatingConstraintsPowerDescriptionListData FunctionType = "operatingConstraintsPowerDescriptionListData" - FunctionTypeOperatingConstraintsPowerLevelListData FunctionType = "operatingConstraintsPowerLevelListData" - FunctionTypeOperatingConstraintsPowerRangeListData FunctionType = "operatingConstraintsPowerRangeListData" - FunctionTypeOperatingConstraintsResumeImplicationListData FunctionType = "operatingConstraintsResumeImplicationListData" - FunctionTypePowerSequenceAlternativesRelationListData FunctionType = "powerSequenceAlternativesRelationListData" - FunctionTypePowerSequenceDescriptionListData FunctionType = "powerSequenceDescriptionListData" - FunctionTypePowerSequenceNodeScheduleInformationData FunctionType = "powerSequenceNodeScheduleInformationData" - FunctionTypePowerSequencePriceCalculationRequestCall FunctionType = "powerSequencePriceCalculationRequestCall" - FunctionTypePowerSequencePriceListData FunctionType = "powerSequencePriceListData" - FunctionTypePowerSequenceScheduleConfigurationRequestCall FunctionType = "powerSequenceScheduleConfigurationRequestCall" - FunctionTypePowerSequenceScheduleConstraintsListData FunctionType = "powerSequenceScheduleConstraintsListData" - FunctionTypePowerSequenceScheduleListData FunctionType = "powerSequenceScheduleListData" - FunctionTypePowerSequenceSchedulePreferenceListData FunctionType = "powerSequenceSchedulePreferenceListData" - FunctionTypePowerSequenceStateListData FunctionType = "powerSequenceStateListData" - FunctionTypePowerTimeSlotScheduleConstraintsListData FunctionType = "powerTimeSlotScheduleConstraintsListData" - FunctionTypePowerTimeSlotScheduleListData FunctionType = "powerTimeSlotScheduleListData" - FunctionTypePowerTimeSlotValueListData FunctionType = "powerTimeSlotValueListData" - FunctionTypeResultData FunctionType = "resultData" - FunctionTypeSensingDescriptionData FunctionType = "sensingDescriptionData" - FunctionTypeSensingListData FunctionType = "sensingListData" - FunctionTypeSessionIdentificationListData FunctionType = "sessionIdentificationListData" - FunctionTypeSessionMeasurementRelationListData FunctionType = "sessionMeasurementRelationListData" - FunctionTypeSetpointConstraintsListData FunctionType = "setpointConstraintsListData" - FunctionTypeSetpointDescriptionListData FunctionType = "setpointDescriptionListData" - FunctionTypeSetpointListData FunctionType = "setpointListData" - FunctionTypeSmartEnergyManagementPsConfigurationRequestCall FunctionType = "smartEnergyManagementPsConfigurationRequestCall" - FunctionTypeSmartEnergyManagementPsData FunctionType = "smartEnergyManagementPsData" - FunctionTypeSmartEnergyManagementPsPriceCalculationRequestCall FunctionType = "smartEnergyManagementPsPriceCalculationRequestCall" - FunctionTypeSmartEnergyManagementPsPriceData FunctionType = "smartEnergyManagementPsPriceData" - FunctionTypeSpecificationVersionListData FunctionType = "specificationVersionListData" - FunctionTypeSubscriptionManagementDeleteCall FunctionType = "subscriptionManagementDeleteCall" - FunctionTypeSubscriptionManagementEntryListData FunctionType = "subscriptionManagementEntryListData" - FunctionTypeSubscriptionManagementRequestCall FunctionType = "subscriptionManagementRequestCall" - FunctionTypeSupplyConditionDescriptionListData FunctionType = "supplyConditionDescriptionListData" - FunctionTypeSupplyConditionListData FunctionType = "supplyConditionListData" - FunctionTypeSupplyConditionThresholdRelationListData FunctionType = "supplyConditionThresholdRelationListData" - FunctionTypeTaskManagementJobDescriptionListData FunctionType = "taskManagementJobDescriptionListData" - FunctionTypeTaskManagementJobListData FunctionType = "taskManagementJobListData" - FunctionTypeTaskManagementJobRelationListData FunctionType = "taskManagementJobRelationListData" - FunctionTypeTaskManagementOverviewData FunctionType = "taskManagementOverviewData" - FunctionTypeThresholdConstraintsListData FunctionType = "thresholdConstraintsListData" - FunctionTypeThresholdDescriptionListData FunctionType = "thresholdDescriptionListData" - FunctionTypeThresholdListData FunctionType = "thresholdListData" - FunctionTypeTimeDistributorData FunctionType = "timeDistributorData" - FunctionTypeTimeDistributorEnquiryCall FunctionType = "timeDistributorEnquiryCall" - FunctionTypeTimeInformationData FunctionType = "timeInformationData" - FunctionTypeTimePrecisionData FunctionType = "timePrecisionData" - FunctionTypeTimeTableConstraintsListData FunctionType = "timeTableConstraintsListData" - FunctionTypeTimeTableDescriptionListData FunctionType = "timeTableDescriptionListData" - FunctionTypeTimeTableListData FunctionType = "timeTableListData" - FunctionTypeDeviceConfigurationKeyValueConstraintsListData FunctionType = "deviceConfigurationKeyValueConstraintsListData" - FunctionTypeDeviceConfigurationKeyValueListData FunctionType = "deviceConfigurationKeyValueListData" - FunctionTypeDeviceConfigurationKeyValueDescriptionListData FunctionType = "deviceConfigurationKeyValueDescriptionListData" - FunctionTypeLoadControlLimitConstraintsListData FunctionType = "loadControlLimitConstraintsListData" - FunctionTypeLoadControlLimitDescriptionListData FunctionType = "loadControlLimitDescriptionListData" - FunctionTypeLoadControlLimitListData FunctionType = "loadControlLimitListData" - FunctionTypeLoadControlNodeData FunctionType = "loadControlNodeData" - FunctionTypeTimeSeriesConstraintsListData FunctionType = "timeSeriesConstraintsListData" - FunctionTypeTimeSeriesDescriptionListData FunctionType = "timeSeriesDescriptionListData" - FunctionTypeTimeSeriesListData FunctionType = "timeSeriesListData" - FunctionTypeTariffOverallConstraintsData FunctionType = "tariffOverallConstraintsData" - FunctionTypeTariffListData FunctionType = "tariffListData" - FunctionTypeTariffBoundaryRelationListData FunctionType = "tariffBoundaryRelationListData" - FunctionTypeTariffTierRelationListData FunctionType = "tariffTierRelationListData" - FunctionTypeTariffDescriptionListData FunctionType = "tariffDescriptionListData" - FunctionTypeTierBoundaryListData FunctionType = "tierBoundaryListData" - FunctionTypeTierBoundaryDescriptionListData FunctionType = "tierBoundaryDescriptionListData" - FunctionTypeCommodityListData FunctionType = "commodityListData" - FunctionTypeTierListData FunctionType = "tierListData" - FunctionTypeTierIncentiveRelationListData FunctionType = "tierIncentiveRelationListData" - FunctionTypeTierDescriptionListData FunctionType = "tierDescriptionListData" - FunctionTypeIncentiveListData FunctionType = "incentiveListData" - FunctionTypeIncentiveDescriptionListData FunctionType = "incentiveDescriptionListData" - FunctionTypeIncentiveTableData FunctionType = "incentiveTableData" - FunctionTypeIncentiveTableDescriptionData FunctionType = "incentiveTableDescriptionData" - FunctionTypeIncentiveTableConstraintsData FunctionType = "incentiveTableConstraintsData" - FunctionTypeElectricalConnectionPermittedValueSetListData FunctionType = "electricalConnectionPermittedValueSetListData" - FunctionTypeUseCaseInformationListData FunctionType = "useCaseInformationListData" - FunctionTypeNodeManagementUseCaseData FunctionType = "nodeManagementUseCaseData" - FunctionTypeBillConstraintsListData FunctionType = "billConstraintsListData" - FunctionTypeBillDescriptionListData FunctionType = "billDescriptionListData" - FunctionTypeBillListData FunctionType = "billListData" - FunctionTypeIdentificationListData FunctionType = "identificationListData" - FunctionTypeMeasurementSeriesListData FunctionType = "measurementSeriesListData" - FunctionTypeElectricalConnectionCharacteristicData FunctionType = "electricalConnectionCharacteristicData" - FunctionTypeElectricalConnectionCharacteristicListData FunctionType = "electricalConnectionCharacteristicListData" - FunctionTypeStateInformationListData FunctionType = "stateInformationListData" -) - -type PossibleOperationsClassifierType struct { - Partial *ElementTagType `json:"partial,omitempty"` -} - -type PossibleOperationsReadType struct { - Partial *ElementTagType `json:"partial,omitempty"` -} - -type PossibleOperationsWriteType struct { - Partial *ElementTagType `json:"partial,omitempty"` -} - -type PossibleOperationsType struct { - Read *PossibleOperationsReadType `json:"read,omitempty"` - Write *PossibleOperationsWriteType `json:"write,omitempty"` -} - -type PossibleOperationsElementsType struct { - Read *ElementTagType `json:"read,omitempty"` - Write *ElementTagType `json:"write,omitempty"` -} - -type FunctionPropertyType struct { - Function *FunctionType `json:"function,omitempty"` - PossibleOperations *PossibleOperationsType `json:"possibleOperations,omitempty"` -} - -type FunctionPropertyElementsType struct { - Function *ElementTagType `json:"function,omitempty"` - PossibleOperations *ElementTagType `json:"possibleOperations,omitempty"` -} diff --git a/spine/model/commondatatypes_additions.go b/spine/model/commondatatypes_additions.go deleted file mode 100644 index 59838ba5..00000000 --- a/spine/model/commondatatypes_additions.go +++ /dev/null @@ -1,234 +0,0 @@ -package model - -import ( - "errors" - "fmt" - "math" - "strconv" - "strings" - "time" - - "github.com/rickb777/date/period" -) - -// TimeType xs:time - -func NewTimeType(t string) *TimeType { - value := TimeType(t) - return &value -} - -func (s *TimeType) GetTime() (time.Time, error) { - allowedFormats := []string{ - "15:04:05.999999999", - "15:04:05.999999999Z", - "15:04:05", - "15:04:05Z", - "15:04:05+07:00", - "15:04:05-07:00", - } - - for _, format := range allowedFormats { - if value, err := time.Parse(format, string(*s)); err == nil { - return value, nil - } - } - - return time.Time{}, errors.New("unsupported time format") -} - -// DateType xs:date - -func NewDateType(t string) *DateType { - value := DateType(t) - return &value -} - -// 2001-10-26, 2001-10-26+02:00, 2001-10-26Z, 2001-10-26+00:00, -2001-10-26, or -20000-04-01 -func (d *DateType) GetTime() (time.Time, error) { - allowedFormats := []string{ - "2006-01-02", - "2006-01-02Z", - "2006-01-02+07:00", - } - - for _, format := range allowedFormats { - if value, err := time.Parse(format, string(*d)); err == nil { - return value, nil - } - } - - return time.Time{}, errors.New("unsupported date format") -} - -// DateTimeType xs:datetime - -func NewDateTimeType(t string) *DateTimeType { - value := DateTimeType(t) - return &value -} - -func NewDateTimeTypeFromTime(t time.Time) *DateTimeType { - s := t.Format(time.RFC3339) - return NewDateTimeType(s) -} - -func (d *DateTimeType) GetTime() (time.Time, error) { - allowedFormats := []string{ - "2006-01-02T15:04:05.999999999", - "2006-01-02T15:04:05.999999999Z", - "2006-01-02T15:04:05", - "2006-01-02T15:04:05Z", - "2006-01-02T15:04:05+07:00", - "2006-01-02T15:04:05-07:00", - time.RFC3339, - } - - for _, format := range allowedFormats { - if value, err := time.Parse(format, string(*d)); err == nil { - return value, nil - } - } - - return time.Time{}, errors.New("unsupported datetime format") -} - -// DurationType - -func NewDurationType(duration time.Duration) *DurationType { - d, _ := period.NewOf(duration) - value := DurationType(d.String()) - return &value -} - -func (d *DurationType) GetTimeDuration() (time.Duration, error) { - return getTimeDurationFromString(string(*d)) -} - -// helper for DurationType and AbsoluteOrRelativeTimeType -func getTimeDurationFromString(s string) (time.Duration, error) { - p, err := period.Parse(string(s)) - if err != nil { - return 0, err - } - - return p.DurationApprox(), nil -} - -// AbsoluteOrRelativeTimeType -// can be of type TimeType or DurationType - -func NewAbsoluteOrRelativeTimeType(s string) *AbsoluteOrRelativeTimeType { - value := AbsoluteOrRelativeTimeType(s) - return &value -} - -func NewAbsoluteOrRelativeTimeTypeFromDuration(t time.Duration) *AbsoluteOrRelativeTimeType { - s := NewDurationType(t) - value := AbsoluteOrRelativeTimeType(*s) - return &value -} - -func NewAbsoluteOrRelativeTimeTypeFromTime(t time.Time) *AbsoluteOrRelativeTimeType { - s := NewDateTimeTypeFromTime(t) - value := AbsoluteOrRelativeTimeType(*s) - return &value -} - -func (a *AbsoluteOrRelativeTimeType) GetDateTimeType() *DateTimeType { - value := NewDateTimeType(string(*a)) - return value -} - -func (a *AbsoluteOrRelativeTimeType) GetTime() (time.Time, error) { - value := NewDateTimeType(string(*a)) - t, err := value.GetTime() - if err == nil { - return t, nil - } - - // Check if this is a relative time - d, err := getTimeDurationFromString(string(*a)) - if err != nil { - return time.Time{}, err - } - r := time.Now().Add(d) - return r, nil -} - -func (a *AbsoluteOrRelativeTimeType) GetDurationType() (*DurationType, error) { - value, err := a.GetTimeDuration() - if err != nil { - return nil, err - } - - return NewDurationType(value), nil -} - -func (a *AbsoluteOrRelativeTimeType) GetTimeDuration() (time.Duration, error) { - return getTimeDurationFromString(string(*a)) -} - -// ScaledNumberType - -func (m *ScaledNumberType) GetValue() float64 { - if m.Number == nil { - return 0 - } - var scale float64 = 0 - if m.Scale != nil { - scale = float64(*m.Scale) - } - return float64(*m.Number) * math.Pow(10, scale) -} - -func NewScaledNumberType(value float64) *ScaledNumberType { - m := &ScaledNumberType{} - - numberOfDecimals := 0 - temp := strconv.FormatFloat(value, 'f', -1, 64) - index := strings.IndexByte(temp, '.') - if index > -1 { - numberOfDecimals = len(temp) - index - 1 - } - - // We limit this to 4 digits for now - if numberOfDecimals > 4 { - numberOfDecimals = 4 - } - - numberValue := NumberType(math.Trunc(value * math.Pow(10, float64(numberOfDecimals)))) - m.Number = &numberValue - - if numberValue != 0 { - scaleValue := ScaleType(-numberOfDecimals) - m.Scale = &scaleValue - } - - return m -} - -// FeatureAddressType - -func (r *FeatureAddressType) String() string { - if r == nil { - return "" - } - - var result string = "" - if r.Device != nil { - result += string(*r.Device) - } - result += ":[" - for index, id := range r.Entity { - if index > 0 { - result += "," - } - result += fmt.Sprintf("%d", id) - } - result += "]:" - if r.Feature != nil { - result += fmt.Sprintf("%d", *r.Feature) - } - return result -} diff --git a/spine/model/commondatatypes_additions_test.go b/spine/model/commondatatypes_additions_test.go deleted file mode 100644 index b29efbf9..00000000 --- a/spine/model/commondatatypes_additions_test.go +++ /dev/null @@ -1,282 +0,0 @@ -package model - -import ( - "testing" - "time" -) - -func TestTimeType(t *testing.T) { - tc := []struct { - in string - parse string - }{ - {"21:32:52.12679", "15:04:05.999999999"}, - {"21:32:52.12679Z", "15:04:05.999999999Z"}, - {"21:32:52", "15:04:05"}, - {"19:32:52Z", "15:04:05Z"}, - {"19:32:52+07:00", "15:04:05+07:00"}, - {"19:32:52-07:00", "15:04:05-07:00"}, - } - - for _, tc := range tc { - got := NewTimeType(tc.in) - expect, err := time.Parse(tc.parse, tc.in) - if err != nil { - t.Errorf("Parsing failure with %s and parser %s: %s", tc.in, tc.parse, err) - continue - } - value, err := got.GetTime() - if err != nil { - t.Errorf("Test Failure with %s and parser %s: %s", tc.in, tc.parse, err) - continue - } - - if value.UTC() != expect.UTC() { - t.Errorf("Test failure for %s, expected %s and got %s", tc.in, value.String(), expect.String()) - } - } -} - -func TestDateType(t *testing.T) { - tc := []struct { - in string - parse string - }{ - {"2022-02-01", "2006-01-02"}, - {"2022-02-01Z", "2006-01-02Z"}, - {"2022-02-01+07:00", "2006-01-02+07:00"}, - } - - for _, tc := range tc { - got := NewDateType(tc.in) - expect, err := time.Parse(tc.parse, tc.in) - if err != nil { - t.Errorf("Parsing failure with %s and parser %s: %s", tc.in, tc.parse, err) - continue - } - value, err := got.GetTime() - if err != nil { - t.Errorf("Test Failure with %s and parser %s: %s", tc.in, tc.parse, err) - continue - } - - if value.UTC() != expect.UTC() { - t.Errorf("Test failure for %s, expected %s and got %s", tc.in, value.String(), expect.String()) - } - } -} - -func TestDateTimeType(t *testing.T) { - tc := []struct { - in string - parse string - }{ - {"2022-02-01T21:32:52.12679", "2006-01-02T15:04:05.999999999"}, - {"2022-02-01T21:32:52.12679Z", "2006-01-02T15:04:05.999999999Z"}, - {"2022-02-01T21:32:52", "2006-01-02T15:04:05"}, - {"2022-02-01T19:32:52Z", "2006-01-02T15:04:05Z"}, - {"2022-02-01T19:32:52+07:00", "2006-01-02T15:04:05+07:00"}, - {"2022-02-01T19:32:52-07:00", "2006-01-02T15:04:05-07:00"}, - } - - for _, tc := range tc { - got := NewDateTimeType(tc.in) - expect, err := time.Parse(tc.parse, tc.in) - if err != nil { - t.Errorf("Parsing failure with %s and parser %s: %s", tc.in, tc.parse, err) - continue - } - value, err := got.GetTime() - if err != nil { - t.Errorf("Test Failure with %s and parser %s: %s", tc.in, tc.parse, err) - continue - } - - if value.UTC() != expect.UTC() { - t.Errorf("Test failure for %s, expected %s and got %s", tc.in, value.String(), expect.String()) - } - } -} - -func TestDurationType(t *testing.T) { - tc := []struct { - in time.Duration - out string - }{ - {time.Duration(4) * time.Second, "PT4S"}, - } - - for _, tc := range tc { - duration := NewDurationType(tc.in) - got, err := duration.GetTimeDuration() - if err != nil { - t.Errorf("Test Failure with %s: %s", tc.in, err) - continue - } - if got != tc.in { - t.Errorf("Test failure for %d, got %d", tc.in, got) - } - if string(*duration) != tc.out { - t.Errorf("Test failure for %d, expected %s got %s", tc.in, tc.out, string(*duration)) - } - } -} - -func TestAbsoluteOrRelativeTimeTypeAbsolute(t *testing.T) { - tc := []struct { - in string - dateTime time.Time - }{ - {"2022-02-01T19:32:52Z", time.Date(2022, 02, 01, 19, 32, 52, 0, time.UTC)}, - } - - for _, tc := range tc { - a := NewAbsoluteOrRelativeTimeType(tc.in) - got, err := a.GetTime() - if err != nil { - t.Errorf("Test Failure with %s: %s", tc.in, err) - continue - } - if got != tc.dateTime { - t.Errorf("Test failure for %s, expected %s got %s", tc.in, tc.dateTime.String(), got.String()) - } - - d := a.GetDateTimeType() - got, err = d.GetTime() - if err != nil { - t.Errorf("Test Failure with %s: %s", tc.in, err) - continue - } - if got != tc.dateTime { - t.Errorf("Test failure for %s, expected %s got %s", tc.in, tc.dateTime.String(), got.String()) - } - } -} - -func TestAbsoluteOrRelativeTimeTypeDuration(t *testing.T) { - tc := []struct { - in time.Duration - out string - }{ - {time.Duration(4) * time.Second, "PT4S"}, - } - - for _, tc := range tc { - a := NewAbsoluteOrRelativeTimeTypeFromDuration(tc.in) - got, err := a.GetDurationType() - if err != nil { - t.Errorf("Test Failure with %d: %s", tc.in, err) - continue - } - if string(*got) != tc.out { - t.Errorf("Test failure for %d, expected %s got %s", tc.in, tc.out, string(*got)) - } - - d, err := a.GetTimeDuration() - if err != nil { - t.Errorf("Test Failure with %d: %s", tc.in, err) - continue - } - got = NewDurationType(d) - if string(*got) != tc.out { - t.Errorf("Test failure for %d, expected %s got %s", tc.in, tc.out, string(*got)) - } - } -} - -func TestAbsoluteOrRelativeTimeTypeRelative(t *testing.T) { - tc := []struct { - in string - out time.Duration - }{ - {"PT4S", time.Duration(4) * time.Second}, - } - - for _, tc := range tc { - a := NewAbsoluteOrRelativeTimeType(tc.in) - got, err := a.GetTimeDuration() - if err != nil { - t.Errorf("Test Failure with %s: %s", tc.in, err) - continue - } - if got != tc.out { - t.Errorf("Test failure for %s, expected %d got %d", tc.in, tc.out, got) - } - - d, err := a.GetDurationType() - if err != nil { - t.Errorf("Test Failure with %s: %s", tc.in, err) - continue - } - got, err = d.GetTimeDuration() - if err != nil { - t.Errorf("Test Failure with %s: %s", tc.in, err) - continue - } - if got != tc.out { - t.Errorf("Test failure for %s, expected %d got %d", tc.in, tc.out, got) - } - } -} - -func TestNewScaledNumberType(t *testing.T) { - tc := []struct { - in float64 - number int64 - scale int - }{ - {0, 0, 0}, - {0.1, 1, -1}, - {1.0, 1, 0}, - {6.25, 625, -2}, - {10, 10, 0}, - {12.5952, 125952, -4}, - {13.1637, 131637, -4}, - } - - for _, tc := range tc { - got := NewScaledNumberType(tc.in) - number := int64(*got.Number) - scale := 0 - if got.Scale != nil { - scale = int(*got.Scale) - } - if number != tc.number || scale != tc.scale { - t.Errorf("NewScaledNumberType(%v) = %d %d, want %d %d", tc.in, got.Number, got.Scale, tc.number, tc.scale) - } - - val := got.GetValue() - if val != tc.in { - t.Errorf("GetValue(%d %d) = %f, want %f", tc.number, tc.scale, val, tc.in) - } - } -} - -func TestFeatureAddressTypeString(t *testing.T) { - tc := []struct { - device AddressDeviceType - entity []AddressEntityType - feature AddressFeatureType - out string - }{ - { - "Device", - []AddressEntityType{1, 1}, - 0, - "Device:[1,1]:0", - }, - } - - for _, tc := range tc { - f := FeatureAddressType{ - Device: &tc.device, - Entity: tc.entity, - Feature: &tc.feature, - } - - got := f.String() - if got != tc.out { - t.Errorf("TestFeatureAddressTypeString(), got %s, expects %s", got, tc.out) - } - } -} diff --git a/spine/model/custom.go b/spine/model/custom.go deleted file mode 100644 index e70ef971..00000000 --- a/spine/model/custom.go +++ /dev/null @@ -1,47 +0,0 @@ -package model - -import ( - "fmt" - - "github.com/enbility/eebus-go/util" -) - -type ErrorType struct { - ErrorNumber ErrorNumberType - Description *DescriptionType -} - -func NewErrorType(errorNumber ErrorNumberType, description string) *ErrorType { - return &ErrorType{ - ErrorNumber: errorNumber, - Description: util.Ptr(DescriptionType(description)), - } -} - -func NewErrorTypeFromNumber(errorNumber ErrorNumberType) *ErrorType { - return &ErrorType{ - ErrorNumber: errorNumber, - } -} - -func NewErrorTypeFromString(description string) *ErrorType { - return NewErrorType(ErrorNumberTypeGeneralError, description) -} - -func NewErrorTypeFromResult(result *ResultDataType) *ErrorType { - if result.ErrorNumber == nil || *result.ErrorNumber == ErrorNumberTypeNoError { - return nil - } - - return &ErrorType{ - ErrorNumber: *result.ErrorNumber, - Description: result.Description, - } -} - -func (e *ErrorType) String() string { - if e.Description != nil && len(*e.Description) > 0 { - return fmt.Sprintf("Error %d: %s", e.ErrorNumber, *e.Description) - } - return fmt.Sprintf("Error %d", e.ErrorNumber) -} diff --git a/spine/model/custom_test.go b/spine/model/custom_test.go deleted file mode 100644 index 6a591fd0..00000000 --- a/spine/model/custom_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestErrorTypeSuite(t *testing.T) { - suite.Run(t, new(ErrorTypeSuite)) -} - -type ErrorTypeSuite struct { - suite.Suite -} - -func (s *ErrorTypeSuite) SetupSuite() {} -func (s *ErrorTypeSuite) TearDownTest() {} - -func (s *ErrorTypeSuite) BeforeTest(suiteName, testName string) {} - -func (s *ErrorTypeSuite) Test_NewErrorType() { - result := NewErrorType(ErrorNumberTypeNoError, "") - assert.NotNil(s.T(), result) -} - -func (s *ErrorTypeSuite) Test_NewErrorTypeFromNumber() { - result := NewErrorTypeFromNumber(ErrorNumberTypeCommandRejected) - assert.NotNil(s.T(), result) -} - -func (s *ErrorTypeSuite) Test_NewErrorTypeFromString() { - result := NewErrorTypeFromString("error") - assert.NotNil(s.T(), result) - - assert.NotEqual(s.T(), 0, len(result.String())) -} - -func (s *ErrorTypeSuite) Test_NewErrorTypeFromResult() { - input := &ResultDataType{} - - result := NewErrorTypeFromResult(input) - assert.Nil(s.T(), result) - - input = &ResultDataType{ - ErrorNumber: util.Ptr(ErrorNumberTypeNoError), - } - - result = NewErrorTypeFromResult(input) - assert.Nil(s.T(), result) - - input = &ResultDataType{ - ErrorNumber: util.Ptr(ErrorNumberTypeCommandNotSupported), - } - - result = NewErrorTypeFromResult(input) - assert.NotNil(s.T(), result) - - assert.NotEqual(s.T(), 0, len(result.String())) -} diff --git a/spine/model/datagram.go b/spine/model/datagram.go deleted file mode 100644 index d51a9c7c..00000000 --- a/spine/model/datagram.go +++ /dev/null @@ -1,26 +0,0 @@ -package model - -type Datagram struct { - Datagram DatagramType `json:"datagram"` -} - -type DatagramType struct { - Header HeaderType `json:"header"` - Payload PayloadType `json:"payload"` -} - -type HeaderType struct { - SpecificationVersion *SpecificationVersionType `json:"specificationVersion,omitempty"` - AddressSource *FeatureAddressType `json:"addressSource,omitempty"` - AddressDestination *FeatureAddressType `json:"addressDestination,omitempty"` - AddressOriginator *FeatureAddressType `json:"addressOriginator,omitempty"` - MsgCounter *MsgCounterType `json:"msgCounter,omitempty"` - MsgCounterReference *MsgCounterType `json:"msgCounterReference,omitempty"` - CmdClassifier *CmdClassifierType `json:"cmdClassifier,omitempty"` - AckRequest *bool `json:"ackRequest,omitempty"` - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` -} - -type PayloadType struct { - Cmd []CmdType `json:"cmd"` -} diff --git a/spine/model/datagram_additions.go b/spine/model/datagram_additions.go deleted file mode 100644 index 9898cbe9..00000000 --- a/spine/model/datagram_additions.go +++ /dev/null @@ -1,42 +0,0 @@ -package model - -import ( - "fmt" -) - -func (d *DatagramType) PrintMessageOverview(send bool, localFeature, remoteFeature string) string { - var result string - - transmission := "Send" - device := "" - if d.Header.AddressDestination != nil && d.Header.AddressDestination.Device != nil { - device = string(*d.Header.AddressDestination.Device) - } - if !send { - transmission = "Recv" - if d.Header.AddressSource.Device != nil { - device = string(*d.Header.AddressSource.Device) - } - device = fmt.Sprintf("%s:%s to %s", device, remoteFeature, localFeature) - } - - cmdClassifier := *d.Header.CmdClassifier - msgCounter := *d.Header.MsgCounter - cmd := d.Payload.Cmd[0] - - switch cmdClassifier { - case CmdClassifierTypeRead: - result = fmt.Sprintf("%s: %s %s %d %s", transmission, device, cmdClassifier, msgCounter, cmd.DataName()) - case CmdClassifierTypeReply: - msgCounterRef := *d.Header.MsgCounterReference - result = fmt.Sprintf("%s: %s %s %d %d %s", transmission, device, cmdClassifier, msgCounter, msgCounterRef, cmd.DataName()) - case CmdClassifierTypeResult: - msgCounterRef := *d.Header.MsgCounterReference - errorNumber := *d.Payload.Cmd[0].ResultData.ErrorNumber - result = fmt.Sprintf("%s: %s %s %d %d %s %d", transmission, device, cmdClassifier, msgCounter, msgCounterRef, cmd.DataName(), errorNumber) - default: - result = fmt.Sprintf("%s: %s %s %d %s", transmission, device, cmdClassifier, msgCounter, cmd.DataName()) - } - - return result -} diff --git a/spine/model/datagram_additions_test.go b/spine/model/datagram_additions_test.go deleted file mode 100644 index 0a423ef9..00000000 --- a/spine/model/datagram_additions_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" -) - -func TestPrintMessageOverview_Read_Send(t *testing.T) { - datagram := &DatagramType{ - Header: HeaderType{ - AddressSource: &FeatureAddressType{ - Device: util.Ptr(AddressDeviceType("localdevice")), - }, - AddressDestination: &FeatureAddressType{ - Device: util.Ptr(AddressDeviceType("localdevice")), - }, - MsgCounter: util.Ptr(MsgCounterType(1)), - CmdClassifier: util.Ptr(CmdClassifierTypeRead), - }, - Payload: PayloadType{ - Cmd: []CmdType{ - {}, - }, - }, - } - - datagram.PrintMessageOverview(false, "", "") -} - -func TestPrintMessageOverview_Read_Recv(t *testing.T) { - datagram := &DatagramType{ - Header: HeaderType{ - AddressSource: &FeatureAddressType{}, - AddressDestination: &FeatureAddressType{ - Device: util.Ptr(AddressDeviceType("localdevice")), - }, - MsgCounter: util.Ptr(MsgCounterType(1)), - CmdClassifier: util.Ptr(CmdClassifierTypeRead), - }, - Payload: PayloadType{ - Cmd: []CmdType{ - {}, - }, - }, - } - - datagram.PrintMessageOverview(true, "", "") -} - -func TestPrintMessageOverview_Reply_Recv(t *testing.T) { - datagram := &DatagramType{ - Header: HeaderType{ - AddressSource: &FeatureAddressType{}, - AddressDestination: &FeatureAddressType{ - Device: util.Ptr(AddressDeviceType("localdevice")), - }, - MsgCounter: util.Ptr(MsgCounterType(1)), - MsgCounterReference: util.Ptr(MsgCounterType(1)), - CmdClassifier: util.Ptr(CmdClassifierTypeReply), - }, - Payload: PayloadType{ - Cmd: []CmdType{ - {}, - }, - }, - } - - datagram.PrintMessageOverview(true, "", "") -} - -func TestPrintMessageOverview_Result_Recv(t *testing.T) { - datagram := &DatagramType{ - Header: HeaderType{ - AddressSource: &FeatureAddressType{}, - AddressDestination: &FeatureAddressType{ - Device: util.Ptr(AddressDeviceType("localdevice")), - }, - MsgCounter: util.Ptr(MsgCounterType(1)), - MsgCounterReference: util.Ptr(MsgCounterType(1)), - CmdClassifier: util.Ptr(CmdClassifierTypeResult), - }, - Payload: PayloadType{ - Cmd: []CmdType{ - { - ResultData: &ResultDataType{ - ErrorNumber: util.Ptr(ErrorNumberType(1)), - }, - }, - }, - }, - } - - datagram.PrintMessageOverview(true, "", "") -} - -func TestPrintMessageOverview_Write_Recv(t *testing.T) { - datagram := &DatagramType{ - Header: HeaderType{ - AddressSource: &FeatureAddressType{}, - AddressDestination: &FeatureAddressType{ - Device: util.Ptr(AddressDeviceType("localdevice")), - }, - MsgCounter: util.Ptr(MsgCounterType(1)), - MsgCounterReference: util.Ptr(MsgCounterType(1)), - CmdClassifier: util.Ptr(CmdClassifierTypeWrite), - }, - Payload: PayloadType{ - Cmd: []CmdType{ - {}, - }, - }, - } - - datagram.PrintMessageOverview(true, "", "") -} diff --git a/spine/model/datatunneling.go b/spine/model/datatunneling.go deleted file mode 100644 index 7246c987..00000000 --- a/spine/model/datatunneling.go +++ /dev/null @@ -1,27 +0,0 @@ -package model - -type PurposeIdType string - -type ChannelIdType uint - -type DataTunnelingHeaderType struct { - PurposeId *PurposeIdType `json:"purposeId,omitempty"` - ChannelId *ChannelIdType `json:"channelId,omitempty"` - SequenceId *uint `json:"sequenceId,omitempty"` -} - -type DataTunnelingHeaderElementsType struct { - PurposeId *ElementTagType `json:"purposeId,omitempty"` - ChannelId *ElementTagType `json:"channelId,omitempty"` - SequenceId *ElementTagType `json:"sequenceId,omitempty"` -} - -type DataTunnelingCallType struct { - Header *DataTunnelingHeaderType `json:"header,omitempty"` - Payload *string `json:"payload,omitempty"` -} - -type DataTunnelingCallElementsType struct { - Header *DataTunnelingHeaderElementsType `json:"header,omitempty"` - Payload *ElementTagType `json:"payload,omitempty"` -} diff --git a/spine/model/deviceclassification.go b/spine/model/deviceclassification.go deleted file mode 100644 index 51e84274..00000000 --- a/spine/model/deviceclassification.go +++ /dev/null @@ -1,55 +0,0 @@ -package model - -type DeviceClassificationStringType string - -type PowerSourceType string - -const ( - PowerSourceTypeUnknown PowerSourceType = "unknown" - PowerSourceTypeMainssinglephase PowerSourceType = "mainsSinglePhase" - PowerSourceTypeMains3Phase PowerSourceType = "mains3Phase" - PowerSourceTypeBattery PowerSourceType = "battery" - PowerSourceTypeDc PowerSourceType = "dc" -) - -type DeviceClassificationManufacturerDataType struct { - DeviceName *DeviceClassificationStringType `json:"deviceName,omitempty"` - DeviceCode *DeviceClassificationStringType `json:"deviceCode,omitempty"` - SerialNumber *DeviceClassificationStringType `json:"serialNumber,omitempty"` - SoftwareRevision *DeviceClassificationStringType `json:"softwareRevision,omitempty"` - HardwareRevision *DeviceClassificationStringType `json:"hardwareRevision,omitempty"` - VendorName *DeviceClassificationStringType `json:"vendorName,omitempty"` - VendorCode *DeviceClassificationStringType `json:"vendorCode,omitempty"` - BrandName *DeviceClassificationStringType `json:"brandName,omitempty"` - PowerSource *PowerSourceType `json:"powerSource,omitempty"` - ManufacturerNodeIdentification *DeviceClassificationStringType `json:"manufacturerNodeIdentification,omitempty"` - ManufacturerLabel *LabelType `json:"manufacturerLabel,omitempty"` - ManufacturerDescription *DescriptionType `json:"manufacturerDescription,omitempty"` -} - -type DeviceClassificationManufacturerDataElementsType struct { - DeviceName *ElementTagType `json:"deviceName,omitempty"` - DeviceCode *ElementTagType `json:"deviceCode,omitempty"` - SerialNumber *ElementTagType `json:"serialNumber,omitempty"` - SoftwareRevision *ElementTagType `json:"softwareRevision,omitempty"` - HardwareRevision *ElementTagType `json:"hardwareRevision,omitempty"` - VendorName *ElementTagType `json:"vendorName,omitempty"` - VendorCode *ElementTagType `json:"vendorCode,omitempty"` - BrandName *ElementTagType `json:"brandName,omitempty"` - PowerSource *ElementTagType `json:"powerSource,omitempty"` - ManufacturerNodeIdentification *ElementTagType `json:"manufacturerNodeIdentification,omitempty"` - ManufacturerLabel *ElementTagType `json:"manufacturerLabel,omitempty"` - ManufacturerDescription *ElementTagType `json:"manufacturerDescription,omitempty"` -} - -type DeviceClassificationUserDataType struct { - UserNodeIdentification *DeviceClassificationStringType `json:"userNodeIdentification,omitempty"` - UserLabel *LabelType `json:"userLabel,omitempty"` - UserDescription *DescriptionType `json:"userDescription,omitempty"` -} - -type DeviceClassificationUserDataElementsType struct { - UserNodeIdentification *ElementTagType `json:"userNodeIdentification,omitempty"` - UserLabel *ElementTagType `json:"userLabel,omitempty"` - UserDescription *ElementTagType `json:"userDescription,omitempty"` -} diff --git a/spine/model/deviceconfiguration.go b/spine/model/deviceconfiguration.go deleted file mode 100644 index 967ea332..00000000 --- a/spine/model/deviceconfiguration.go +++ /dev/null @@ -1,150 +0,0 @@ -package model - -type DeviceConfigurationKeyIdType uint - -type DeviceConfigurationKeyValueStringType string - -type DeviceConfigurationKeyNameType string - -const ( - DeviceConfigurationKeyNameTypePeakPowerOfPVSystem DeviceConfigurationKeyNameType = "peakPowerOfPvSystem" - DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor DeviceConfigurationKeyNameType = "pvCurtailmentLimitFactor" - DeviceConfigurationKeyNameTypeAsymmetricChargingSupported DeviceConfigurationKeyNameType = "asymmetricChargingSupported" - DeviceConfigurationKeyNameTypeCommunicationsStandard DeviceConfigurationKeyNameType = "communicationsStandard" - DeviceConfigurationKeyNameTypeInverterGridCode DeviceConfigurationKeyNameType = "inverterGridCode" - DeviceConfigurationKeyNameTypePvStringAvailabilityStatus DeviceConfigurationKeyNameType = "pvStringAvailabilityStatus" - DeviceConfigurationKeyNameTypeBatteryAvailabilityStatus DeviceConfigurationKeyNameType = "batteryAvailabilityStatus" - DeviceConfigurationKeyNameTypeGridConnectionStatus DeviceConfigurationKeyNameType = "gridConnectionStatus" - DeviceConfigurationKeyNameTypeTimeToAcChargePowerMax DeviceConfigurationKeyNameType = "timeToAcChargePowerMax" - DeviceConfigurationKeyNameTypeTimeToAcDischargePowerMax DeviceConfigurationKeyNameType = "timeToAcDischargePowerMax" - DeviceConfigurationKeyNameTypeTilt DeviceConfigurationKeyNameType = "tilt" - DeviceConfigurationKeyNameTypeAzimuth DeviceConfigurationKeyNameType = "azimuth" - DeviceConfigurationKeyNameTypeBatteryType DeviceConfigurationKeyNameType = "batteryType" - DeviceConfigurationKeyNameTypeMaxCycleCountPerDay DeviceConfigurationKeyNameType = "maxCycleCountPerDay" - DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit DeviceConfigurationKeyNameType = "failsafeConsumptionActivePowerLimit" - DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit DeviceConfigurationKeyNameType = "failsafeProductionActivePowerLimit" - DeviceConfigurationKeyNameTypeFailsafePositiveReactivePowerLimit DeviceConfigurationKeyNameType = "failsafePositiveReactivePowerLimit" - DeviceConfigurationKeyNameTypeFailsafeNegativeReactivePowerLimit DeviceConfigurationKeyNameType = "failsafeNegativeReactivePowerLimit" - DeviceConfigurationKeyNameTypeFailsafePositiveCosPhiLimit DeviceConfigurationKeyNameType = "failsafePositiveCosPhiLimit" - DeviceConfigurationKeyNameTypeFailsafeNegativeCosPhiLimit DeviceConfigurationKeyNameType = "failsafeNegativeCosPhiLimit" - DeviceConfigurationKeyNameTypeMaxAcChargePower DeviceConfigurationKeyNameType = "maxAcChargePower" - DeviceConfigurationKeyNameTypeMaxAcDischargePower DeviceConfigurationKeyNameType = "maxAcDischargePower" - DeviceConfigurationKeyNameTypeMaxDcChargePower DeviceConfigurationKeyNameType = "maxDcChargePower" - DeviceConfigurationKeyNameTypeMaxDcDischargePower DeviceConfigurationKeyNameType = "maxDcDischargePower" - DeviceConfigurationKeyNameTypeBatteryActiveControlMode DeviceConfigurationKeyNameType = "batteryActiveControlMode" - DeviceConfigurationKeyNameTypeDefaultAcPower DeviceConfigurationKeyNameType = "defaultAcPower" - DeviceConfigurationKeyNameTypeDefaultDcPower DeviceConfigurationKeyNameType = "defaultDcPower" - DeviceConfigurationKeyNameTypeDefaultPccPower DeviceConfigurationKeyNameType = "defaultPccPower" - DeviceConfigurationKeyNameTypeFailsafeAcPowerSetpoint DeviceConfigurationKeyNameType = "failsafeAcPowerSetpoint" - DeviceConfigurationKeyNameTypeFailsafeDcPowerSetpoint DeviceConfigurationKeyNameType = "failsafeDcPowerSetpoint" - DeviceConfigurationKeyNameTypeFailsafePccPowerSetpoint DeviceConfigurationKeyNameType = "failsafePccPowerSetpoint" - DeviceConfigurationKeyNameTypeFailsafeDurationMinimum DeviceConfigurationKeyNameType = "failsafeDurationMinimum" - DeviceConfigurationKeyNameTypeDischargingBelowTargetEnergyRequestPermitted DeviceConfigurationKeyNameType = "dischargingBelowTargetEnergyRequestPermitted" - DeviceConfigurationKeyNameTypeIncentivesSimulationCyclesMax DeviceConfigurationKeyNameType = "incentivesSimulationCyclesMax" - DeviceConfigurationKeyNameTypeIncentivesSimulationConcurrent DeviceConfigurationKeyNameType = "incentivesSimulationConcurrent" - DeviceConfigurationKeyNameTypeIncentivesTimeoutIncentiveRequest DeviceConfigurationKeyNameType = "incentivesTimeoutIncentiveRequest" - DeviceConfigurationKeyNameTypeIncentivesWaitIncentiveWriteable DeviceConfigurationKeyNameType = "incentivesWaitIncentiveWriteable" -) - -type DeviceConfigurationKeyValueTypeType string - -const ( - DeviceConfigurationKeyValueTypeTypeBoolean DeviceConfigurationKeyValueTypeType = "boolean" - DeviceConfigurationKeyValueTypeTypeDate DeviceConfigurationKeyValueTypeType = "date" - DeviceConfigurationKeyValueTypeTypeDateTime DeviceConfigurationKeyValueTypeType = "dateTime" - DeviceConfigurationKeyValueTypeTypeDuration DeviceConfigurationKeyValueTypeType = "duration" - DeviceConfigurationKeyValueTypeTypeString DeviceConfigurationKeyValueTypeType = "string" - DeviceConfigurationKeyValueTypeTypeTime DeviceConfigurationKeyValueTypeType = "time" - DeviceConfigurationKeyValueTypeTypeScaledNumber DeviceConfigurationKeyValueTypeType = "scaledNumber" - DeviceConfigurationKeyValueTypeTypeInteger DeviceConfigurationKeyValueTypeType = "integer" -) - -type DeviceConfigurationKeyValueValueType struct { - Boolean *bool `json:"boolean,omitempty"` - Date *DateType `json:"date,omitempty"` - DateTime *DateTimeType `json:"dateTime,omitempty"` - Duration *DurationType `json:"duration,omitempty"` - String *DeviceConfigurationKeyValueStringType `json:"string,omitempty"` - Time *TimeType `json:"time,omitempty"` - ScaledNumber *ScaledNumberType `json:"scaledNumber,omitempty"` - Integer *int64 `json:"integer,omitempty"` -} - -type DeviceConfigurationKeyValueValueElementsType struct { - Boolean *ElementTagType `json:"boolean,omitempty"` - Date *ElementTagType `json:"date,omitempty"` - DateTime *ElementTagType `json:"dateTime,omitempty"` - Duration *ElementTagType `json:"duration,omitempty"` - String *ElementTagType `json:"string,omitempty"` - Time *ElementTagType `json:"time,omitempty"` - ScaledNumber *ScaledNumberElementsType `json:"scaledNumber,omitempty"` -} - -type DeviceConfigurationKeyValueDataType struct { - KeyId *DeviceConfigurationKeyIdType `json:"keyId,omitempty" eebus:"key"` - Value *DeviceConfigurationKeyValueValueType `json:"value,omitempty"` - IsValueChangeable *bool `json:"isValueChangeable,omitempty"` -} - -type DeviceConfigurationKeyValueDataElementsType struct { - KeyId *ElementTagType `json:"keyId,omitempty"` - Value *DeviceConfigurationKeyValueValueElementsType `json:"value,omitempty"` - IsValueChangeable *ElementTagType `json:"isValueChangeable,omitempty"` -} - -type DeviceConfigurationKeyValueListDataType struct { - DeviceConfigurationKeyValueData []DeviceConfigurationKeyValueDataType `json:"deviceConfigurationKeyValueData,omitempty"` -} - -type DeviceConfigurationKeyValueListDataSelectorsType struct { - KeyId *DeviceConfigurationKeyIdType `json:"keyId,omitempty"` -} - -type DeviceConfigurationKeyValueDescriptionDataType struct { - KeyId *DeviceConfigurationKeyIdType `json:"keyId,omitempty" eebus:"key"` - KeyName *DeviceConfigurationKeyNameType `json:"keyName,omitempty"` - ValueType *DeviceConfigurationKeyValueTypeType `json:"valueType,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type DeviceConfigurationKeyValueDescriptionDataElementsType struct { - KeyId *ElementTagType `json:"keyId,omitempty"` - KeyName *ElementTagType `json:"keyName,omitempty"` - ValueType *ElementTagType `json:"valueType,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type DeviceConfigurationKeyValueDescriptionListDataType struct { - DeviceConfigurationKeyValueDescriptionData []DeviceConfigurationKeyValueDescriptionDataType `json:"deviceConfigurationKeyValueDescriptionData,omitempty"` -} - -type DeviceConfigurationKeyValueDescriptionListDataSelectorsType struct { - KeyId *DeviceConfigurationKeyIdType `json:"keyId,omitempty"` - KeyName *string `json:"keyName,omitempty"` -} - -type DeviceConfigurationKeyValueConstraintsDataType struct { - KeyId *DeviceConfigurationKeyIdType `json:"keyId,omitempty" eebus:"key"` - ValueRangeMin *DeviceConfigurationKeyValueValueType `json:"valueRangeMin,omitempty"` - ValueRangeMax *DeviceConfigurationKeyValueValueType `json:"valueRangeMax,omitempty"` - ValueStepSize *DeviceConfigurationKeyValueValueType `json:"valueStepSize,omitempty"` -} - -type DeviceConfigurationKeyValueConstraintsDataElementsType struct { - KeyId *ElementTagType `json:"keyId,omitempty"` - ValueRangeMin *DeviceConfigurationKeyValueValueElementsType `json:"valueRangeMin,omitempty"` - ValueRangeMax *DeviceConfigurationKeyValueValueElementsType `json:"valueRangeMax,omitempty"` - ValueStepSize *DeviceConfigurationKeyValueValueElementsType `json:"valueStepSize,omitempty"` -} - -type DeviceConfigurationKeyValueConstraintsListDataType struct { - DeviceConfigurationKeyValueConstraintsData []DeviceConfigurationKeyValueConstraintsDataType `json:"deviceConfigurationKeyValueConstraintsData,omitempty"` -} - -type DeviceConfigurationKeyValueConstraintsListDataSelectorsType struct { - KeyId *DeviceConfigurationKeyIdType `json:"keyId,omitempty"` -} diff --git a/spine/model/deviceconfiguration_additions.go b/spine/model/deviceconfiguration_additions.go deleted file mode 100644 index 07cc2475..00000000 --- a/spine/model/deviceconfiguration_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// DeviceConfigurationKeyValueListDataType - -var _ Updater = (*DeviceConfigurationKeyValueListDataType)(nil) - -func (r *DeviceConfigurationKeyValueListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []DeviceConfigurationKeyValueDataType - if newList != nil { - newData = newList.(*DeviceConfigurationKeyValueListDataType).DeviceConfigurationKeyValueData - } - - r.DeviceConfigurationKeyValueData = UpdateList(r.DeviceConfigurationKeyValueData, newData, filterPartial, filterDelete) -} - -// DeviceConfigurationKeyValueDescriptionListDataType - -var _ Updater = (*DeviceConfigurationKeyValueDescriptionListDataType)(nil) - -func (r *DeviceConfigurationKeyValueDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []DeviceConfigurationKeyValueDescriptionDataType - if newList != nil { - newData = newList.(*DeviceConfigurationKeyValueDescriptionListDataType).DeviceConfigurationKeyValueDescriptionData - } - - r.DeviceConfigurationKeyValueDescriptionData = UpdateList(r.DeviceConfigurationKeyValueDescriptionData, newData, filterPartial, filterDelete) -} - -// DeviceConfigurationKeyValueConstraintsListDataType - -var _ Updater = (*DeviceConfigurationKeyValueConstraintsListDataType)(nil) - -func (r *DeviceConfigurationKeyValueConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []DeviceConfigurationKeyValueConstraintsDataType - if newList != nil { - newData = newList.(*DeviceConfigurationKeyValueConstraintsListDataType).DeviceConfigurationKeyValueConstraintsData - } - - r.DeviceConfigurationKeyValueConstraintsData = UpdateList(r.DeviceConfigurationKeyValueConstraintsData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/deviceconfiguration_additions_test.go b/spine/model/deviceconfiguration_additions_test.go deleted file mode 100644 index d509b809..00000000 --- a/spine/model/deviceconfiguration_additions_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestDeviceConfigurationKeyValueListDataType_Update(t *testing.T) { - sut := DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []DeviceConfigurationKeyValueDataType{ - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(0)), - Value: &DeviceConfigurationKeyValueValueType{ - Boolean: util.Ptr(true), - }, - }, - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), - Value: &DeviceConfigurationKeyValueValueType{ - Boolean: util.Ptr(true), - }, - }, - }, - } - - newData := DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []DeviceConfigurationKeyValueDataType{ - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), - Value: &DeviceConfigurationKeyValueValueType{ - Boolean: util.Ptr(false), - }, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.DeviceConfigurationKeyValueData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.KeyId)) - assert.Equal(t, true, *item1.Value.Boolean) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.KeyId)) - assert.Equal(t, false, *item2.Value.Boolean) -} - -func TestDeviceConfigurationKeyValueDescriptionListDataType_Update(t *testing.T) { - sut := DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(0)), - ValueType: util.Ptr(DeviceConfigurationKeyValueTypeTypeBoolean), - }, - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), - ValueType: util.Ptr(DeviceConfigurationKeyValueTypeTypeBoolean), - }, - }, - } - - newData := DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), - ValueType: util.Ptr(DeviceConfigurationKeyValueTypeTypeString), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.DeviceConfigurationKeyValueDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.KeyId)) - assert.Equal(t, DeviceConfigurationKeyValueTypeTypeBoolean, *item1.ValueType) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.KeyId)) - assert.Equal(t, DeviceConfigurationKeyValueTypeTypeString, *item2.ValueType) -} - -func TestDeviceConfigurationKeyValueConstraintsListDataType_Update(t *testing.T) { - sut := DeviceConfigurationKeyValueConstraintsListDataType{ - DeviceConfigurationKeyValueConstraintsData: []DeviceConfigurationKeyValueConstraintsDataType{ - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(0)), - ValueStepSize: &DeviceConfigurationKeyValueValueType{ - Boolean: util.Ptr(true), - }, - }, - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), - ValueStepSize: &DeviceConfigurationKeyValueValueType{ - Boolean: util.Ptr(true), - }, - }, - }, - } - - newData := DeviceConfigurationKeyValueConstraintsListDataType{ - DeviceConfigurationKeyValueConstraintsData: []DeviceConfigurationKeyValueConstraintsDataType{ - { - KeyId: util.Ptr(DeviceConfigurationKeyIdType(1)), - ValueStepSize: &DeviceConfigurationKeyValueValueType{ - Boolean: util.Ptr(false), - }, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.DeviceConfigurationKeyValueConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.KeyId)) - assert.Equal(t, true, *item1.ValueStepSize.Boolean) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.KeyId)) - assert.Equal(t, false, *item2.ValueStepSize.Boolean) -} diff --git a/spine/model/devicediagnosis.go b/spine/model/devicediagnosis.go deleted file mode 100644 index da5faba7..00000000 --- a/spine/model/devicediagnosis.go +++ /dev/null @@ -1,76 +0,0 @@ -package model - -type VendorStateCodeType string - -type LastErrorCodeType string - -type DeviceDiagnosisOperatingStateType string - -const ( - DeviceDiagnosisOperatingStateTypeNormalOperation DeviceDiagnosisOperatingStateType = "normalOperation" - DeviceDiagnosisOperatingStateTypeStandby DeviceDiagnosisOperatingStateType = "standby" - DeviceDiagnosisOperatingStateTypeFailure DeviceDiagnosisOperatingStateType = "failure" - DeviceDiagnosisOperatingStateTypeServiceNeeded DeviceDiagnosisOperatingStateType = "serviceNeeded" - DeviceDiagnosisOperatingStateTypeOverrideDetected DeviceDiagnosisOperatingStateType = "overrideDetected" - DeviceDiagnosisOperatingStateTypeInAlarm DeviceDiagnosisOperatingStateType = "inAlarm" - DeviceDiagnosisOperatingStateTypeNotReachable DeviceDiagnosisOperatingStateType = "notReachable" - DeviceDiagnosisOperatingStateTypeFinished DeviceDiagnosisOperatingStateType = "finished" - DeviceDiagnosisOperatingStateTypeTemporarilyNotReady DeviceDiagnosisOperatingStateType = "temporarilyNotReady" - DeviceDiagnosisOperatingStateTypeOff DeviceDiagnosisOperatingStateType = "off" -) - -type PowerSupplyConditionType string - -const ( - PowerSupplyConditionTypeGood PowerSupplyConditionType = "good" - PowerSupplyConditionTypeLow PowerSupplyConditionType = "low" - PowerSupplyConditionTypeCritical PowerSupplyConditionType = "critical" - PowerSupplyConditionTypeUnknown PowerSupplyConditionType = "unknown" - PowerSupplyConditionTypeError PowerSupplyConditionType = "error" -) - -type DeviceDiagnosisStateDataType struct { - Timestamp *string `json:"timestamp,omitempty"` - OperatingState *DeviceDiagnosisOperatingStateType `json:"operatingState,omitempty"` - VendorStateCode *VendorStateCodeType `json:"vendorStateCode,omitempty"` - LastErrorCode *LastErrorCodeType `json:"lastErrorCode,omitempty"` - UpTime *DurationType `json:"upTime,omitempty"` - TotalUpTime *DurationType `json:"totalUpTime,omitempty"` - PowerSupplyCondition *PowerSupplyConditionType `json:"powerSupplyCondition,omitempty"` -} - -type DeviceDiagnosisStateDataElementsType struct { - Timestamp *ElementTagType `json:"timestamp,omitempty"` - OperatingState *ElementTagType `json:"operatingState,omitempty"` - VendorStateCode *ElementTagType `json:"vendorStateCode,omitempty"` - LastErrorCode *ElementTagType `json:"lastErrorCode,omitempty"` - UpTime *ElementTagType `json:"upTime,omitempty"` - TotalUpTime *ElementTagType `json:"totalUpTime,omitempty"` - PowerSupplyCondition *ElementTagType `json:"powerSupplyCondition,omitempty"` -} - -type DeviceDiagnosisHeartbeatDataType struct { - Timestamp *string `json:"timestamp,omitempty"` - HeartbeatCounter *uint64 `json:"heartbeatCounter,omitempty"` - HeartbeatTimeout *DurationType `json:"heartbeatTimeout,omitempty"` -} - -type DeviceDiagnosisHeartbeatDataElementsType struct { - Timestamp *ElementTagType `json:"timestamp,omitempty"` - HeartbeatCounter *ElementTagType `json:"heartbeatCounter,omitempty"` - HeartbeatTimeout *ElementTagType `json:"heartbeatTimeout,omitempty"` -} - -type DeviceDiagnosisServiceDataType struct { - Timestamp *string `json:"timestamp,omitempty"` - InstallationTime *string `json:"installationTime,omitempty"` - BootCounter *uint64 `json:"bootCounter,omitempty"` - NextService *string `json:"nextService,omitempty"` -} - -type DeviceDiagnosisServiceDataElementsType struct { - Timestamp *ElementTagType `json:"timestamp,omitempty"` - InstallationTime *ElementTagType `json:"installationTime,omitempty"` - BootCounter *ElementTagType `json:"bootCounter,omitempty"` - NextService *ElementTagType `json:"nextService,omitempty"` -} diff --git a/spine/model/directcontrol.go b/spine/model/directcontrol.go deleted file mode 100644 index 47ab26f3..00000000 --- a/spine/model/directcontrol.go +++ /dev/null @@ -1,55 +0,0 @@ -package model - -type DirectControlActivityStateType string - -const ( - DirectControlActivityStateTypeRunning AlarmTypeType = "running" - DirectControlActivityStateTypePaused AlarmTypeType = "paused" - DirectControlActivityStateTypeInactive AlarmTypeType = "inactive" -) - -type DirectControlActivityDataType struct { - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - ActivityState *DirectControlActivityStateType `json:"activityState,omitempty"` - IsActivityStateChangeable *bool `json:"isActivityStateChangeable,omitempty"` - EnergyMode *EnergyModeType `json:"energyMode,omitempty"` - IsEnergyModeChangeable *bool `json:"isEnergyModeChangeable,omitempty"` - Power *ScaledNumberType `json:"power,omitempty"` - IsPowerChangeable *bool `json:"isPowerChangeable,omitempty"` - Energy *ScaledNumberType `json:"energy,omitempty"` - IsEnergyChangeable *bool `json:"isEnergyChangeable,omitempty"` - Sequence_id *PowerSequenceIdType `json:"sequence_id,omitempty"` -} - -type DirectControlActivityDataElementsType struct { - Timestamp *ElementTagType `json:"timestamp,omitempty"` - ActivityState *ElementTagType `json:"activityState,omitempty"` - IsActivityStateChangeable *ElementTagType `json:"isActivityStateChangeable,omitempty"` - EnergyMode *ElementTagType `json:"energyMode,omitempty"` - IsEnergyModeChangeable *ElementTagType `json:"isEnergyModeChangeable,omitempty"` - Power *ScaledNumberElementsType `json:"power,omitempty"` - IsPowerChangeable *ElementTagType `json:"isPowerChangeable,omitempty"` - Energy *ScaledNumberElementsType `json:"energy,omitempty"` - IsEnergyChangeable *ElementTagType `json:"isEnergyChangeable,omitempty"` - Sequence_id *ElementTagType `json:"sequence_id,omitempty"` -} - -type DirectControlActivityListDataType struct { - DirectControlActivityDataElements []DirectControlActivityDataType `json:"directControlActivityDataElements,omitempty"` -} - -type DirectControlActivityListDataSelectorsType struct { - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` -} - -type DirectControlDescriptionDataType struct { - PositiveEnergyDirection *EnergyDirectionType `json:"positiveEnergyDirection,omitempty"` - PowerUnit *UnitOfMeasurementType `json:"powerUnit,omitempty"` - EnergyUnit *UnitOfMeasurementType `json:"energyUnit,omitempty"` -} - -type DirectControlDescriptionDataElementsType struct { - PositiveEnergyDirection *ElementTagType `json:"positiveEnergyDirection,omitempty"` - PowerUnit *ElementTagType `json:"powerUnit,omitempty"` - EnergyUnit *ElementTagType `json:"energyUnit,omitempty"` -} diff --git a/spine/model/eebus_tags.go b/spine/model/eebus_tags.go deleted file mode 100644 index ed0e95d2..00000000 --- a/spine/model/eebus_tags.go +++ /dev/null @@ -1,46 +0,0 @@ -package model - -import ( - "reflect" - "strings" - - "github.com/enbility/eebus-go/logging" -) - -type EEBusTag string - -const ( - EEBusTagFunction EEBusTag = "fct" - EEBusTagType EEBusTag = "typ" - EEBusTagKey EEBusTag = "key" -) - -type EEBusTagTypeType string - -const ( - EEBusTagTypeTypeSelector EEBusTagTypeType = "selector" - EEbusTagTypeTypeElements EEBusTagTypeType = "elements" -) - -const EEBusTagName string = "eebus" - -func EEBusTags(field reflect.StructField) map[EEBusTag]string { - result := make(map[EEBusTag]string) - tags := field.Tag.Get(EEBusTagName) - if len(tags) == 0 { - return result - } - for _, tag := range strings.Split(tags, ",") { - pair := strings.Split(tag, ":") - if len(pair) == 1 { - // boolean tags like "key" - result[EEBusTag(pair[0])] = "true" - } else if len(pair) == 2 { - result[EEBusTag(pair[0])] = pair[1] - } else { - logging.Log().Errorf("error: malformatted eebus tag: '%s'", tags) - } - } - - return result -} diff --git a/spine/model/electricalconnection.go b/spine/model/electricalconnection.go deleted file mode 100644 index 95be03e9..00000000 --- a/spine/model/electricalconnection.go +++ /dev/null @@ -1,241 +0,0 @@ -package model - -type ElectricalConnectionIdType uint - -type ElectricalConnectionParameterIdType uint - -type ElectricalConnectionCharaceteristicsIdType uint - -type ElectricalConnectionMeasurandVariantType string - -const ( - ElectricalConnectionMeasurandVariantTypeAmplitude ElectricalConnectionMeasurandVariantType = "amplitude" - ElectricalConnectionMeasurandVariantTypeRms ElectricalConnectionMeasurandVariantType = "rms" - ElectricalConnectionMeasurandVariantTypeInstantaneous ElectricalConnectionMeasurandVariantType = "instantaneous" - ElectricalConnectionMeasurandVariantTypeAngle ElectricalConnectionMeasurandVariantType = "angle" - ElectricalConnectionMeasurandVariantTypeCosphi ElectricalConnectionMeasurandVariantType = "cosPhi" -) - -type ElectricalConnectionVoltageTypeType string - -const ( - ElectricalConnectionVoltageTypeTypeAc ElectricalConnectionVoltageTypeType = "ac" - ElectricalConnectionVoltageTypeTypeDc ElectricalConnectionVoltageTypeType = "dc" -) - -type ElectricalConnectionAcMeasurementTypeType string - -const ( - ElectricalConnectionAcMeasurementTypeTypeReal ElectricalConnectionAcMeasurementTypeType = "real" - ElectricalConnectionAcMeasurementTypeTypeReactive ElectricalConnectionAcMeasurementTypeType = "reactive" - ElectricalConnectionAcMeasurementTypeTypeApparent ElectricalConnectionAcMeasurementTypeType = "apparent" - ElectricalConnectionAcMeasurementTypeTypePhase ElectricalConnectionAcMeasurementTypeType = "phase" -) - -type ElectricalConnectionPhaseNameType string - -const ( - ElectricalConnectionPhaseNameTypeA ElectricalConnectionPhaseNameType = "a" - ElectricalConnectionPhaseNameTypeB ElectricalConnectionPhaseNameType = "b" - ElectricalConnectionPhaseNameTypeC ElectricalConnectionPhaseNameType = "c" - ElectricalConnectionPhaseNameTypeAb ElectricalConnectionPhaseNameType = "ab" - ElectricalConnectionPhaseNameTypeBc ElectricalConnectionPhaseNameType = "bc" - ElectricalConnectionPhaseNameTypeAc ElectricalConnectionPhaseNameType = "ac" - ElectricalConnectionPhaseNameTypeAbc ElectricalConnectionPhaseNameType = "abc" - ElectricalConnectionPhaseNameTypeNeutral ElectricalConnectionPhaseNameType = "neutral" - ElectricalConnectionPhaseNameTypeGround ElectricalConnectionPhaseNameType = "ground" - ElectricalConnectionPhaseNameTypeNone ElectricalConnectionPhaseNameType = "none" -) - -type ElectricalConnectionConnectionPointType string - -const ( - ElectricalConnectionConnectionPointTypeGrid ElectricalConnectionConnectionPointType = "grid" - ElectricalConnectionConnectionPointTypeHome ElectricalConnectionConnectionPointType = "home" - ElectricalConnectionConnectionPointTypePv ElectricalConnectionConnectionPointType = "pv" - ElectricalConnectionConnectionPointTypeSd ElectricalConnectionConnectionPointType = "sd" - ElectricalConnectionConnectionPointTypeOther ElectricalConnectionConnectionPointType = "other" -) - -type ElectricalConnectionCharacteristicIdType uint - -type ElectricalConnectionCharacteristicContextType string - -const ( - ElectricalConnectionCharacteristicContextTypeDevice ElectricalConnectionCharacteristicContextType = "device" - ElectricalConnectionCharacteristicContextTypeEntity ElectricalConnectionCharacteristicContextType = "entity" - ElectricalConnectionCharacteristicContextTypeInverter ElectricalConnectionCharacteristicContextType = "inverter" - ElectricalConnectionCharacteristicContextTypePvString ElectricalConnectionCharacteristicContextType = "pvString" - ElectricalConnectionCharacteristicContextTypeBattery ElectricalConnectionCharacteristicContextType = "battery" -) - -type ElectricalConnectionCharacteristicTypeType string - -const ( - ElectricalConnectionCharacteristicTypeTypePowerConsumptionMin ElectricalConnectionCharacteristicTypeType = "powerConsumptionMin" - ElectricalConnectionCharacteristicTypeTypePowerConsumptionMax ElectricalConnectionCharacteristicTypeType = "powerConsumptionMax" - ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMin ElectricalConnectionCharacteristicTypeType = "powerConsumptionNominalMin" - ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax ElectricalConnectionCharacteristicTypeType = "powerConsumptionNominalMax" - ElectricalConnectionCharacteristicTypeTypePowerProductionMin ElectricalConnectionCharacteristicTypeType = "powerProductionMin" - ElectricalConnectionCharacteristicTypeTypePowerProductionMax ElectricalConnectionCharacteristicTypeType = "powerProductionMax" - ElectricalConnectionCharacteristicTypeTypePowerProductionNominalMin ElectricalConnectionCharacteristicTypeType = "powerProductionNominalMin" - ElectricalConnectionCharacteristicTypeTypePowerProductionNominalMax ElectricalConnectionCharacteristicTypeType = "powerProductionNominalMax" - ElectricalConnectionCharacteristicTypeTypeEnergyCapacityNominalMax ElectricalConnectionCharacteristicTypeType = "energyCapacityNominalMax" - ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax ElectricalConnectionCharacteristicTypeType = "contractualConsumptionNominalMax" - ElectricalConnectionCharacteristicTypeTypeContracutalProductionNominalMax ElectricalConnectionCharacteristicTypeType = "contractualProductionNominalMax" - ElectricalConnectionCharacteristicTypeTypeApparentPowerProductionNominalMax ElectricalConnectionCharacteristicTypeType = "apparentPowerProductionNominalMax" - ElectricalConnectionCharacteristicTypeTypeApparentPowerConsumptionNominalMax ElectricalConnectionCharacteristicTypeType = "apparentPowerConsumptionNominalMax" -) - -type ElectricalConnectionParameterDescriptionDataType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty" eebus:"key"` - ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty" eebus:"key"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - VoltageType *ElectricalConnectionVoltageTypeType `json:"voltageType,omitempty"` - AcMeasuredPhases *ElectricalConnectionPhaseNameType `json:"acMeasuredPhases,omitempty"` - AcMeasuredInReferenceTo *ElectricalConnectionPhaseNameType `json:"acMeasuredInReferenceTo,omitempty"` - AcMeasurementType *ElectricalConnectionAcMeasurementTypeType `json:"acMeasurementType,omitempty"` - AcMeasurementVariant *ElectricalConnectionMeasurandVariantType `json:"acMeasurementVariant,omitempty"` - AcMeasuredHarmonic *uint8 `json:"acMeasuredHarmonic,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type ElectricalConnectionParameterDescriptionDataElementsType struct { - ElectricalConnectionId *ElementTagType `json:"electricalConnectionId,omitempty"` - ParameterId *ElementTagType `json:"parameterId,omitempty"` - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - VoltageType *ElementTagType `json:"voltageType,omitempty"` - AcMeasuredPhases *ElementTagType `json:"acMeasuredPhases,omitempty"` - AcMeasuredInReferenceTo *ElementTagType `json:"acMeasuredInReferenceTo,omitempty"` - AcMeasurementType *ElementTagType `json:"acMeasurementType,omitempty"` - AcMeasurementVariant *ElementTagType `json:"acMeasurementVariant,omitempty"` - AcMeasuredHarmonic *ElementTagType `json:"acMeasuredHarmonic,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type ElectricalConnectionParameterDescriptionListDataType struct { - ElectricalConnectionParameterDescriptionData []ElectricalConnectionParameterDescriptionDataType `json:"electricalConnectionParameterDescriptionData,omitempty"` -} - -type ElectricalConnectionParameterDescriptionListDataSelectorsType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty"` - ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} - -type ElectricalConnectionPermittedValueSetDataType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty" eebus:"key"` - ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty" eebus:"key"` - PermittedValueSet []ScaledNumberSetType `json:"permittedValueSet,omitempty"` -} - -type ElectricalConnectionPermittedValueSetDataElementsType struct { - ElectricalConnectionId *ElementTagType `json:"electricalConnectionId,omitempty"` - ParameterId *ElementTagType `json:"parameterId,omitempty"` - PermittedValueSet *ElementTagType `json:"permittedValueSet,omitempty"` -} - -type ElectricalConnectionPermittedValueSetListDataType struct { - ElectricalConnectionPermittedValueSetData []ElectricalConnectionPermittedValueSetDataType `json:"electricalConnectionPermittedValueSetData,omitempty"` -} - -type ElectricalConnectionPermittedValueSetListDataSelectorsType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty"` - ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty"` -} - -type ElectricalConnectionStateDataType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty" eebus:"key"` - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - CurrentEnergyMode *EnergyModeType `json:"currentEnergyMode,omitempty"` - ConsumptionTime *DurationType `json:"consumptionTime,omitempty"` - ProductionTime *DurationType `json:"productionTime,omitempty"` - TotalConsumptionTime *DurationType `json:"totalConsumptionTime,omitempty"` - TotalProductionTime *DurationType `json:"totalProductionTime,omitempty"` -} - -type ElectricalConnectionStateDataElementsType struct { - ElectricalConnectionId *ElementTagType `json:"electricalConnectionId,omitempty"` - Timestamp *ElementTagType `json:"timestamp,omitempty"` - CurrentEnergyMode *ElementTagType `json:"currentEnergyMode,omitempty"` - ConsumptionTime *ElementTagType `json:"consumptionTime,omitempty"` - ProductionTime *ElementTagType `json:"productionTime,omitempty"` - TotalConsumptionTime *ElementTagType `json:"totalConsumptionTime,omitempty"` - TotalProductionTime *ElementTagType `json:"totalProductionTime,omitempty"` -} - -type ElectricalConnectionStateListDataType struct { - ElectricalConnectionStateData []ElectricalConnectionStateDataType `json:"electricalConnectionStateData,omitempty"` -} - -type ElectricalConnectionStateListDataSelectorsType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty"` -} - -type ElectricalConnectionDescriptionDataType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty" eebus:"key"` - PowerSupplyType *ElectricalConnectionVoltageTypeType `json:"powerSupplyType,omitempty"` - AcConnectedPhases *uint `json:"acConnectedPhases,omitempty"` - AcRmsPeriodDuration *DurationType `json:"acRmsPeriodDuration,omitempty"` - PositiveEnergyDirection *EnergyDirectionType `json:"positiveEnergyDirection,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type ElectricalConnectionDescriptionDataElementsType struct { - ElectricalConnectionId *ElementTagType `json:"electricalConnectionId,omitempty"` - PowerSupplyType *ElementTagType `json:"powerSupplyType,omitempty"` - AcConnectedPhases *ElementTagType `json:"acConnectedPhases,omitempty"` - AcRmsPeriodDuration *ElementTagType `json:"acRmsPeriodDuration,omitempty"` - PositiveEnergyDirection *ElementTagType `json:"positiveEnergyDirection,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type ElectricalConnectionDescriptionListDataType struct { - ElectricalConnectionDescriptionData []ElectricalConnectionDescriptionDataType `json:"electricalConnectionDescriptionData,omitempty"` -} - -type ElectricalConnectionDescriptionListDataSelectorsType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} - -type ElectricalConnectionCharacteristicDataType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty" eebus:"key"` - ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty" eebus:"key"` - CharacteristicId *ElectricalConnectionCharaceteristicsIdType `json:"characteristicId,omitempty" eebus:"key"` - CharacteristicContext *ElectricalConnectionCharacteristicContextType `json:"characteristicContext,omitempty"` - CharacteristicType *ElectricalConnectionCharacteristicTypeType `json:"characteristicType,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` -} - -type ElectricalConnectionCharacteristicDataElementsType struct { - ElectricalConnectionId *ElementTagType `json:"electricalConnectionId,omitempty"` - ParameterId *ElementTagType `json:"parameterId,omitempty"` - CharacteristicId *ElementTagType `json:"characteristicId,omitempty"` - CharacteristicContext *ElementTagType `json:"characteristicContext,omitempty"` - CharacteristicType *ElementTagType `json:"characteristicType,omitempty"` - Value *ScaledNumberElementsType `json:"value,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` -} - -type ElectricalConnectionCharacteristicListDataType struct { - ElectricalConnectionCharacteristicListData []ElectricalConnectionCharacteristicDataType `json:"electricalConnectionCharacteristicListData,omitempty"` -} - -type ElectricalConnectionCharacteristicListDataSelectorsType struct { - ElectricalConnectionId *ElectricalConnectionIdType `json:"electricalConnectionId,omitempty"` - ParameterId *ElectricalConnectionParameterIdType `json:"parameterId,omitempty"` - CharacteristicId *ElectricalConnectionCharaceteristicsIdType `json:"characteristicId,omitempty"` - CharacteristicContext *ElectricalConnectionCharacteristicContextType `json:"characteristicContext,omitempty"` - CharacteristicType *ElectricalConnectionCharacteristicTypeType `json:"characteristicType,omitempty"` -} diff --git a/spine/model/electricalconnection_additions.go b/spine/model/electricalconnection_additions.go deleted file mode 100644 index 52e9e8d0..00000000 --- a/spine/model/electricalconnection_additions.go +++ /dev/null @@ -1,66 +0,0 @@ -package model - -// ElectricalConnectionStateListDataType - -var _ Updater = (*ElectricalConnectionStateListDataType)(nil) - -func (r *ElectricalConnectionStateListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []ElectricalConnectionStateDataType - if newList != nil { - newData = newList.(*ElectricalConnectionStateListDataType).ElectricalConnectionStateData - } - - r.ElectricalConnectionStateData = UpdateList(r.ElectricalConnectionStateData, newData, filterPartial, filterDelete) -} - -// ElectricalConnectionPermittedValueSetListDataType - -var _ Updater = (*ElectricalConnectionPermittedValueSetListDataType)(nil) - -func (r *ElectricalConnectionPermittedValueSetListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []ElectricalConnectionPermittedValueSetDataType - if newList != nil { - newData = newList.(*ElectricalConnectionPermittedValueSetListDataType).ElectricalConnectionPermittedValueSetData - } - - r.ElectricalConnectionPermittedValueSetData = UpdateList(r.ElectricalConnectionPermittedValueSetData, newData, filterPartial, filterDelete) -} - -// ElectricalConnectionDescriptionListDataType - -var _ Updater = (*ElectricalConnectionDescriptionListDataType)(nil) - -func (r *ElectricalConnectionDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []ElectricalConnectionDescriptionDataType - if newList != nil { - newData = newList.(*ElectricalConnectionDescriptionListDataType).ElectricalConnectionDescriptionData - } - - r.ElectricalConnectionDescriptionData = UpdateList(r.ElectricalConnectionDescriptionData, newData, filterPartial, filterDelete) -} - -// ElectricalConnectionCharacteristicListDataType - -var _ Updater = (*ElectricalConnectionCharacteristicListDataType)(nil) - -func (r *ElectricalConnectionCharacteristicListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []ElectricalConnectionCharacteristicDataType - if newList != nil { - newData = newList.(*ElectricalConnectionCharacteristicListDataType).ElectricalConnectionCharacteristicListData - } - - r.ElectricalConnectionCharacteristicListData = UpdateList(r.ElectricalConnectionCharacteristicListData, newData, filterPartial, filterDelete) -} - -// ElectricalConnectionParameterDescriptionListDataType - -var _ Updater = (*ElectricalConnectionParameterDescriptionListDataType)(nil) - -func (r *ElectricalConnectionParameterDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []ElectricalConnectionParameterDescriptionDataType - if newList != nil { - newData = newList.(*ElectricalConnectionParameterDescriptionListDataType).ElectricalConnectionParameterDescriptionData - } - - r.ElectricalConnectionParameterDescriptionData = UpdateList(r.ElectricalConnectionParameterDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/electricalconnection_additions_test.go b/spine/model/electricalconnection_additions_test.go deleted file mode 100644 index 38e95d74..00000000 --- a/spine/model/electricalconnection_additions_test.go +++ /dev/null @@ -1,1188 +0,0 @@ -package model - -import ( - "encoding/json" - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestElectricalConnectionStateListDataType_Update(t *testing.T) { - sut := ElectricalConnectionStateListDataType{ - ElectricalConnectionStateData: []ElectricalConnectionStateDataType{ - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - CurrentEnergyMode: util.Ptr(EnergyModeTypeProduce), - }, - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), - CurrentEnergyMode: util.Ptr(EnergyModeTypeProduce), - }, - }, - } - - newData := ElectricalConnectionStateListDataType{ - ElectricalConnectionStateData: []ElectricalConnectionStateDataType{ - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), - CurrentEnergyMode: util.Ptr(EnergyModeTypeConsume), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ElectricalConnectionStateData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, EnergyModeTypeProduce, *item1.CurrentEnergyMode) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ElectricalConnectionId)) - assert.Equal(t, EnergyModeTypeConsume, *item2.CurrentEnergyMode) -} - -// verifies that a subset of existing items will be updated with identified new values -func TestElectricalConnectionPermittedValueSetListDataType_Update_Modify(t *testing.T) { - sut := ElectricalConnectionPermittedValueSetListDataType{ - ElectricalConnectionPermittedValueSetData: []ElectricalConnectionPermittedValueSetDataType{ - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(0)), - PermittedValueSet: []ScaledNumberSetType{ - { - Range: []ScaledNumberRangeType{ - { - Min: NewScaledNumberType(1), - }, - }, - }, - }, - }, - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(1)), - PermittedValueSet: []ScaledNumberSetType{ - { - Range: []ScaledNumberRangeType{ - { - Min: NewScaledNumberType(6), - Max: NewScaledNumberType(16), - }, - }, - }, - }, - }, - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(2)), - PermittedValueSet: []ScaledNumberSetType{ - { - Range: []ScaledNumberRangeType{ - { - Min: NewScaledNumberType(6), - Max: NewScaledNumberType(16), - }, - }, - }, - }, - }, - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(3)), - PermittedValueSet: []ScaledNumberSetType{ - { - Range: []ScaledNumberRangeType{ - { - Min: NewScaledNumberType(6), - Max: NewScaledNumberType(16), - }, - }, - }, - }, - }, - }, - } - - newData := ElectricalConnectionPermittedValueSetListDataType{ - ElectricalConnectionPermittedValueSetData: []ElectricalConnectionPermittedValueSetDataType{ - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(1)), - PermittedValueSet: []ScaledNumberSetType{ - { - Range: []ScaledNumberRangeType{ - { - Min: NewScaledNumberType(2), - Max: NewScaledNumberType(16), - }, - }, - }, - }, - }, - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(2)), - PermittedValueSet: []ScaledNumberSetType{ - { - Range: []ScaledNumberRangeType{ - { - Min: NewScaledNumberType(2), - Max: NewScaledNumberType(16), - }, - }, - }, - }, - }, - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(3)), - PermittedValueSet: []ScaledNumberSetType{ - { - Range: []ScaledNumberRangeType{ - { - Min: NewScaledNumberType(2), - Max: NewScaledNumberType(16), - }, - }, - }, - }, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ElectricalConnectionPermittedValueSetData - // check the non changing items - assert.Equal(t, 4, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 0, int(*item1.ParameterId)) - assert.Equal(t, 1, len(item1.PermittedValueSet)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 0, int(*item2.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item2.ParameterId)) - assert.Equal(t, 1, len(item2.PermittedValueSet)) - valueSet := item2.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - rangeSet := valueSet.Range[0] - assert.Equal(t, 2.0, rangeSet.Min.GetValue()) - assert.Equal(t, 16.0, rangeSet.Max.GetValue()) -} - -// verifies that a subset of existing items will be updated with identified new values -func TestElectricalConnectionPermittedValueSetListDataType_Update_Modify_Selector(t *testing.T) { - existingDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "electricalConnectionId":0, - "parameterId":0, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":1,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":1, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":2, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":3, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var sut ElectricalConnectionPermittedValueSetListDataType - err := json.Unmarshal([]byte(existingDataJson), &sut) - if assert.Nil(t, err) == false { - return - } - - newDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":2,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var newData ElectricalConnectionPermittedValueSetListDataType - err = json.Unmarshal([]byte(newDataJson), &newData) - if assert.Nil(t, err) == false { - return - } - - partial := &FilterType{ - CmdControl: &CmdControlType{ - Partial: &ElementTagType{}, - }, - ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), - ParameterId: util.Ptr[ElectricalConnectionParameterIdType](1), - }, - } - - // Act - sut.UpdateList(&newData, partial, nil) - - data := sut.ElectricalConnectionPermittedValueSetData - // check the non changing items - assert.Equal(t, 4, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 0, int(*item1.ParameterId)) - assert.Equal(t, 1, len(item1.PermittedValueSet)) - item3 := data[2] - assert.Equal(t, 0, int(*item3.ElectricalConnectionId)) - assert.Equal(t, 2, int(*item3.ParameterId)) - assert.Equal(t, 1, len(item3.PermittedValueSet)) - valueSet := item3.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - rangeSet := valueSet.Range[0] - assert.Equal(t, 6.0, rangeSet.Min.GetValue()) - assert.Equal(t, 16.0, rangeSet.Max.GetValue()) - - // check properties of updated item - item2 := sut.ElectricalConnectionPermittedValueSetData[1] - assert.Equal(t, 0, int(*item2.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item2.ParameterId)) - assert.Equal(t, 1, len(item2.PermittedValueSet)) - valueSet = item2.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - rangeSet = valueSet.Range[0] - assert.Equal(t, 2.0, rangeSet.Min.GetValue()) - assert.Equal(t, 16.0, rangeSet.Max.GetValue()) -} - -// verifies that a subset of existing items will be updated with identified new values -func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Modify(t *testing.T) { - existingDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "electricalConnectionId":0, - "parameterId":0, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":1,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":1, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":2, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":3, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var sut ElectricalConnectionPermittedValueSetListDataType - err := json.Unmarshal([]byte(existingDataJson), &sut) - if assert.Nil(t, err) == false { - return - } - - newDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "electricalConnectionId":0, - "parameterId":1, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":2,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":2, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":2,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":3, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":2,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var newData ElectricalConnectionPermittedValueSetListDataType - err = json.Unmarshal([]byte(newDataJson), &newData) - if assert.Nil(t, err) == false { - return - } - - delete := &FilterType{ - CmdControl: &CmdControlType{ - Delete: &ElementTagType{}, - }, - ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), - ParameterId: util.Ptr[ElectricalConnectionParameterIdType](0), - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), delete) - - data := sut.ElectricalConnectionPermittedValueSetData - // check the deleted item is gone - assert.Equal(t, 3, len(data)) - // check properties of updated item - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item1.ParameterId)) - assert.Equal(t, 1, len(item1.PermittedValueSet)) - valueSet := item1.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - rangeSet := valueSet.Range[0] - assert.Equal(t, 2.0, rangeSet.Min.GetValue()) - assert.Equal(t, 16.0, rangeSet.Max.GetValue()) -} - -// verifies that a subset of existing items will be updated with identified new values -func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete(t *testing.T) { - existingDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "electricalConnectionId":0, - "parameterId":0, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":1,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":1, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":2, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":3, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var sut ElectricalConnectionPermittedValueSetListDataType - err := json.Unmarshal([]byte(existingDataJson), &sut) - if assert.Nil(t, err) == false { - return - } - - delete := &FilterType{ - CmdControl: &CmdControlType{ - Delete: &ElementTagType{}, - }, - ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), - ParameterId: util.Ptr[ElectricalConnectionParameterIdType](0), - }, - } - - // Act - sut.UpdateList(nil, nil, delete) - - data := sut.ElectricalConnectionPermittedValueSetData - // check the deleted item is added again - assert.Equal(t, 3, len(data)) - // check properties of remaining item - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item1.ParameterId)) - assert.Equal(t, 1, len(item1.PermittedValueSet)) - valueSet := item1.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - rangeSet := valueSet.Range[0] - assert.Equal(t, 6.0, rangeSet.Min.GetValue()) - assert.Equal(t, 16.0, rangeSet.Max.GetValue()) -} - -// verifies that a subset of existing items will be updated with identified new values -func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Element(t *testing.T) { - existingDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "electricalConnectionId":0, - "parameterId":0, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":1,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":1, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":2, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":3, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var sut ElectricalConnectionPermittedValueSetListDataType - err := json.Unmarshal([]byte(existingDataJson), &sut) - if assert.Nil(t, err) == false { - return - } - - delete := &FilterType{ - CmdControl: &CmdControlType{ - Delete: &ElementTagType{}, - }, - ElectricalConnectionPermittedValueSetDataElements: &ElectricalConnectionPermittedValueSetDataElementsType{ - PermittedValueSet: &ElementTagType{}, - }, - ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), - ParameterId: util.Ptr[ElectricalConnectionParameterIdType](0), - }, - } - - // Act - sut.UpdateList(nil, nil, delete) - - data := sut.ElectricalConnectionPermittedValueSetData - // check no items are deleted - assert.Equal(t, 4, len(data)) - // check permitted value is removed from item with ID 0 - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 0, int(*item1.ParameterId)) - var nilValue []ScaledNumberSetType - assert.Equal(t, nilValue, item1.PermittedValueSet) - - // check properties of remaining item - item2 := data[1] - assert.Equal(t, 0, int(*item2.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item2.ParameterId)) - assert.Equal(t, 1, len(item2.PermittedValueSet)) - valueSet := item2.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - rangeSet := valueSet.Range[0] - assert.Equal(t, 6.0, rangeSet.Min.GetValue()) - assert.Equal(t, 16.0, rangeSet.Max.GetValue()) -} - -// verifies that a subset of existing items will be updated with identified new values -func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_OnlyElement(t *testing.T) { - existingDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "electricalConnectionId":0, - "parameterId":0, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":1,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":1, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":2, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":3, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var sut ElectricalConnectionPermittedValueSetListDataType - err := json.Unmarshal([]byte(existingDataJson), &sut) - if assert.Nil(t, err) == false { - return - } - - delete := &FilterType{ - CmdControl: &CmdControlType{ - Delete: &ElementTagType{}, - }, - ElectricalConnectionPermittedValueSetDataElements: &ElectricalConnectionPermittedValueSetDataElementsType{ - PermittedValueSet: &ElementTagType{}, - }, - } - - // Act - sut.UpdateList(nil, nil, delete) - - data := sut.ElectricalConnectionPermittedValueSetData - // check no items are deleted - assert.Equal(t, 4, len(data)) - // check permitted value is removed from item with ID 0 - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 0, int(*item1.ParameterId)) - var nilValue []ScaledNumberSetType - assert.Equal(t, nilValue, item1.PermittedValueSet) - - // check properties - item2 := data[1] - assert.Equal(t, 0, int(*item2.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item2.ParameterId)) - assert.Equal(t, nilValue, item2.PermittedValueSet) - - item3 := data[2] - assert.Equal(t, 0, int(*item3.ElectricalConnectionId)) - assert.Equal(t, 2, int(*item3.ParameterId)) - assert.Equal(t, nilValue, item3.PermittedValueSet) - - item4 := data[3] - assert.Equal(t, 0, int(*item4.ElectricalConnectionId)) - assert.Equal(t, 3, int(*item4.ParameterId)) - assert.Equal(t, nilValue, item4.PermittedValueSet) -} - -// verifies that a subset of existing items will be updated with identified new values -func TestElectricalConnectionPermittedValueSetListDataType_Update_Delete_Add(t *testing.T) { - existingDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "electricalConnectionId":0, - "parameterId":0, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":1,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":1, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":2, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":3, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":6,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var sut ElectricalConnectionPermittedValueSetListDataType - err := json.Unmarshal([]byte(existingDataJson), &sut) - if assert.Nil(t, err) == false { - return - } - - newDataJson := `{ - "electricalConnectionPermittedValueSetData":[ - { - "electricalConnectionId":0, - "parameterId":0, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":1,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":1, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":2,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":2, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":2,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - }, - { - "electricalConnectionId":0, - "parameterId":3, - "permittedValueSet":[ - { - "range":[ - { - "min":{"number":2,"scale":0}, - "max":{"number":16,"scale":0} - } - ] - } - ] - } - ] - }` - - var newData ElectricalConnectionPermittedValueSetListDataType - err = json.Unmarshal([]byte(newDataJson), &newData) - if assert.Nil(t, err) == false { - return - } - - delete := &FilterType{ - CmdControl: &CmdControlType{ - Delete: &ElementTagType{}, - }, - ElectricalConnectionPermittedValueSetListDataSelectors: &ElectricalConnectionPermittedValueSetListDataSelectorsType{ - ElectricalConnectionId: util.Ptr[ElectricalConnectionIdType](0), - ParameterId: util.Ptr[ElectricalConnectionParameterIdType](0), - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), delete) - - data := sut.ElectricalConnectionPermittedValueSetData - // check the deleted item is added again - assert.Equal(t, 4, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 0, int(*item1.ParameterId)) - assert.Equal(t, 1, len(item1.PermittedValueSet)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 0, int(*item2.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item2.ParameterId)) - assert.Equal(t, 1, len(item2.PermittedValueSet)) - valueSet := item2.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - rangeSet := valueSet.Range[0] - assert.Equal(t, 2.0, rangeSet.Min.GetValue()) - assert.Equal(t, 16.0, rangeSet.Max.GetValue()) -} - -// verifies that an item in the payload which is not in the existing data will be added -func TestElectricalConnectionPermittedValueSetListDataType_Update_NewItem(t *testing.T) { - existingDataJson := `{ - "electricalConnectionPermittedValueSetData": [ - { - "electricalConnectionId": 1, - "parameterId": 1, - "permittedValueSet": [ - { - "range": [ - { - "min": { "number": 3, "scale": 0 }, - "max": { "number": 6, "scale": 0 } - } - ] - } - ] - } - ] - }` - - var sut ElectricalConnectionPermittedValueSetListDataType - err := json.Unmarshal([]byte(existingDataJson), &sut) - if assert.Nil(t, err) == false { - return - } - - newDataJson := `{ - "electricalConnectionPermittedValueSetData": [ - { - "electricalConnectionId": 1, - "parameterId": 2, - "permittedValueSet": [ - { - "range": [ - { - "min": { "number": 9, "scale": 0 }, - "max": { "number": 19, "scale": 0 } - } - ] - }, - { - "range": [ - { - "min": { "number": 30, "scale": 0 }, - "max": { "number": 36, "scale": 0 } - } - ] - } - ] - } - ] - }` - - var newData ElectricalConnectionPermittedValueSetListDataType - err = json.Unmarshal([]byte(newDataJson), &newData) - if assert.Nil(t, err) == false { - return - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ElectricalConnectionPermittedValueSetData - // new item should be added - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 1, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item1.ParameterId)) - assert.Equal(t, 1, len(item1.PermittedValueSet)) - // check properties of added item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ElectricalConnectionId)) - assert.Equal(t, 2, int(*item2.ParameterId)) - assert.Equal(t, 2, len(item2.PermittedValueSet)) -} - -// verifies that an item in the payload which has no identifiers will be copied to all existing data -// (see EEBus_SPINE_TS_ProtocolSpecification.pdf, Table 7: Considered cmdOptions combinations for classifier "notify") -func TestElectricalConnectionPermittedValueSetListDataType_UpdateWithoutIdenifiers(t *testing.T) { - existingDataJson := `{ - "electricalConnectionPermittedValueSetData": [ - { - "electricalConnectionId": 1, - "parameterId": 1, - "permittedValueSet": [ - { - "range": [ - { - "min": { "number": 3, "scale": 0 }, - "max": { "number": 6, "scale": 0 } - } - ] - } - ] - }, - { - "electricalConnectionId": 1, - "parameterId": 2, - "permittedValueSet": [ - { - "range": [ - { - "min": { "number": 6, "scale": 0 }, - "max": { "number": 12, "scale": 0 } - } - ] - } - ] - } ] - }` - - var sut ElectricalConnectionPermittedValueSetListDataType - err := json.Unmarshal([]byte(existingDataJson), &sut) - if assert.Nil(t, err) == false { - return - } - - // item with no identifiers - newDataJson := `{ - "electricalConnectionPermittedValueSetData": [ - { - "permittedValueSet": [ - { - "range": [ - { - "min": { "number": 30, "scale": 0 }, - "max": { "number": 36, "scale": 0 } - } - ] - } - ] - } - ] - }` - - var newData ElectricalConnectionPermittedValueSetListDataType - err = json.Unmarshal([]byte(newDataJson), &newData) - if assert.Nil(t, err) == false { - return - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ElectricalConnectionPermittedValueSetData - // the new item should not be added - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 1, int(*item1.ElectricalConnectionId)) - assert.Equal(t, 1, int(*item1.ParameterId)) - assert.Equal(t, 1, len(item1.PermittedValueSet)) - valueSet := item1.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - // the values of the item in the payload should be copied to the first item - assert.Equal(t, 30, int(*valueSet.Range[0].Min.Number)) - assert.Equal(t, 0, int(*valueSet.Range[0].Min.Scale)) - assert.Equal(t, 36, int(*valueSet.Range[0].Max.Number)) - assert.Equal(t, 0, int(*valueSet.Range[0].Max.Scale)) - - item2 := data[1] - assert.Equal(t, 1, int(*item2.ElectricalConnectionId)) - assert.Equal(t, 2, int(*item2.ParameterId)) - assert.Equal(t, 1, len(item2.PermittedValueSet)) - valueSet = item2.PermittedValueSet[0] - assert.Equal(t, 1, len(valueSet.Range)) - // the values of the item in the payload should be also copied to the second item - assert.Equal(t, 30, int(*valueSet.Range[0].Min.Number)) - assert.Equal(t, 0, int(*valueSet.Range[0].Min.Scale)) - assert.Equal(t, 36, int(*valueSet.Range[0].Max.Number)) - assert.Equal(t, 0, int(*valueSet.Range[0].Max.Scale)) -} - -func TestElectricalConnectionDescriptionListDataType_Update(t *testing.T) { - sut := ElectricalConnectionDescriptionListDataType{ - ElectricalConnectionDescriptionData: []ElectricalConnectionDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - PowerSupplyType: util.Ptr(ElectricalConnectionVoltageTypeTypeAc), - }, - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), - PowerSupplyType: util.Ptr(ElectricalConnectionVoltageTypeTypeAc), - }, - }, - } - - newData := ElectricalConnectionDescriptionListDataType{ - ElectricalConnectionDescriptionData: []ElectricalConnectionDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), - PowerSupplyType: util.Ptr(ElectricalConnectionVoltageTypeTypeDc), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ElectricalConnectionDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, ElectricalConnectionVoltageTypeTypeAc, *item1.PowerSupplyType) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ElectricalConnectionId)) - assert.Equal(t, ElectricalConnectionVoltageTypeTypeDc, *item2.PowerSupplyType) -} - -func TestElectricalConnectionParameterDescriptionListDataType_Update(t *testing.T) { - sut := ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []ElectricalConnectionParameterDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(0)), - VoltageType: util.Ptr(ElectricalConnectionVoltageTypeTypeAc), - }, - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(0)), - MeasurementId: util.Ptr(MeasurementIdType(0)), - VoltageType: util.Ptr(ElectricalConnectionVoltageTypeTypeAc), - }, - }, - } - - newData := ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []ElectricalConnectionParameterDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(ElectricalConnectionIdType(1)), - ParameterId: util.Ptr(ElectricalConnectionParameterIdType(0)), - VoltageType: util.Ptr(ElectricalConnectionVoltageTypeTypeDc), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ElectricalConnectionParameterDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ElectricalConnectionId)) - assert.Equal(t, ElectricalConnectionVoltageTypeTypeAc, *item1.VoltageType) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ElectricalConnectionId)) - assert.Equal(t, ElectricalConnectionVoltageTypeTypeDc, *item2.VoltageType) -} diff --git a/spine/model/hvac.go b/spine/model/hvac.go deleted file mode 100644 index 060d3d55..00000000 --- a/spine/model/hvac.go +++ /dev/null @@ -1,222 +0,0 @@ -package model - -type HvacSystemFunctionIdType uint - -type HvacSystemFunctionTypeType string - -const ( - HvacSystemFunctionTypeTypeHeating HvacSystemFunctionTypeType = "heating" - HvacSystemFunctionTypeTypeCooling HvacSystemFunctionTypeType = "cooling" - HvacSystemFunctionTypeTypeVentilation HvacSystemFunctionTypeType = "ventilation" - HvacSystemFunctionTypeTypeDhw HvacSystemFunctionTypeType = "dhw" -) - -type HvacOperationModeIdType uint - -type HvacOperationModeTypeType string - -const ( - HvacOperationModeTypeTypeAuto HvacOperationModeTypeType = "auto" - HvacOperationModeTypeTypeOn HvacOperationModeTypeType = "on" - HvacOperationModeTypeTypeOff HvacOperationModeTypeType = "off" - HvacOperationModeTypeTypeEco HvacOperationModeTypeType = "eco" -) - -type HvacOverrunIdType uint - -type HvacOverrunTypeType string - -const ( - HvacOverrunTypeTypeOneTimeDhw HvacOverrunTypeType = "oneTimeDhw" - HvacOverrunTypeTypeParty HvacOverrunTypeType = "party" - HvacOverrunTypeTypeSgReadyCondition1 HvacOverrunTypeType = "sgReadyCondition1" - HvacOverrunTypeTypeSgReadyCondition3 HvacOverrunTypeType = "sgReadyCondition3" - HvacOverrunTypeTypeSgReadyCondition4 HvacOverrunTypeType = "sgReadyCondition4" - HvacOverrunTypeTypeOneDayAway HvacOverrunTypeType = "oneDayAway" - HvacOverrunTypeTypeOneDayAtHome HvacOverrunTypeType = "oneDayAtHome" - HvacOverrunTypeTypeOneTimeVentilation HvacOverrunTypeType = "oneTimeVentilation" - HvacOverrunTypeTypeHvacSystemOff HvacOverrunTypeType = "hvacSystemOff" - HvacOverrunTypeTypeValveKick HvacOverrunTypeType = "valveKick" -) - -type HvacOverrunStatusType string - -const ( - HvacOverrunStatusTypeActive HvacOverrunStatusType = "active" - HvacOverrunStatusTypeRunning HvacOverrunStatusType = "running" - HvacOverrunStatusTypeFinished HvacOverrunStatusType = "finished" - HvacOverrunStatusTypeInactive HvacOverrunStatusType = "inactive" -) - -type HvacSystemFunctionDataType struct { - SystemFunctionId *HvacSystemFunctionIdType `json:"systemFunctionId,omitempty" eebus:"key"` - CurrentOperationModeId *HvacOperationModeIdType `json:"currentOperationModeId,omitempty"` - IsOperationModeIdChangeable *bool `json:"isOperationModeIdChangeable,omitempty"` - CurrentSetpointId *SetpointIdType `json:"currentSetpointId,omitempty"` - IsSetpointIdChangeable *bool `json:"isSetpointIdChangeable,omitempty"` - IsOverrunActive *bool `json:"isOverrunActive,omitempty"` -} - -type HvacSystemFunctionDataElementsType struct { - SystemFunctionId *ElementTagType `json:"systemFunctionId,omitempty"` - CurrentOperationModeId *ElementTagType `json:"currentOperationModeId,omitempty"` - IsOperationModeIdChangeable *ElementTagType `json:"isOperationModeIdChangeable,omitempty"` - CurrentSetpointId *ElementTagType `json:"currentSetpointId,omitempty"` - IsSetpointIdChangeable *ElementTagType `json:"isSetpointIdChangeable,omitempty"` - IsOverrunActive *ElementTagType `json:"isOverrunActive,omitempty"` -} - -type HvacSystemFunctionListDataType struct { - HvacSystemFunctionData []HvacSystemFunctionDataType `json:"hvacSystemFunctionData,omitempty"` -} - -type HvacSystemFunctionListDataSelectorsType struct { - SystemFunctionId []HvacSystemFunctionIdType `json:"systemFunctionId,omitempty"` -} - -type HvacSystemFunctionOperationModeRelationDataType struct { - SystemFunctionId *HvacSystemFunctionIdType `json:"systemFunctionId,omitempty" eebus:"key"` - OperationModeId *HvacOperationModeIdType `json:"operationModeId,omitempty"` -} - -type HvacSystemFunctionOperationModeRelationDataElementsType struct { - SystemFunctionId *ElementTagType `json:"systemFunctionId,omitempty"` - OperationModeId *ElementTagType `json:"operationModeId,omitempty"` -} - -type HvacSystemFunctionOperationModeRelationListDataType struct { - HvacSystemFunctionOperationModeRelationData []HvacSystemFunctionOperationModeRelationDataType `json:"hvacSystemFunctionOperationModeRelationData,omitempty"` -} - -type HvacSystemFunctionOperationModeRelationListDataSelectorsType struct { - SystemFunctionId []HvacSystemFunctionIdType `json:"systemFunctionId,omitempty"` -} - -type HvacSystemFunctionSetpointRelationDataType struct { - SystemFunctionId *HvacSystemFunctionIdType `json:"systemFunctionId,omitempty" eebus:"key"` - OperationModeId *HvacOperationModeIdType `json:"operationModeId,omitempty"` - SetpointId *SetpointIdType `json:"setpointId,omitempty"` -} - -type HvacSystemFunctionSetpointRelationDataElementsType struct { - SystemFunctionId *ElementTagType `json:"systemFunctionId,omitempty"` - OperationModeId *ElementTagType `json:"operationModeId,omitempty"` - SetpointId *ElementTagType `json:"setpointId,omitempty"` -} - -type HvacSystemFunctionSetpointRelationListDataType struct { - HvacSystemFunctionSetpointRelationData []HvacSystemFunctionSetpointRelationDataType `json:"hvacSystemFunctionSetpointRelationData,omitempty"` -} - -type HvacSystemFunctionSetpointRelationListDataSelectorsType struct { - SystemFunctionId *HvacSystemFunctionIdType `json:"systemFunctionId,omitempty"` - OperationModeId *HvacOperationModeIdType `json:"operationModeId,omitempty"` -} - -type HvacSystemFunctionPowerSequenceRelationDataType struct { - SystemFunctionId *HvacSystemFunctionIdType `json:"systemFunctionId,omitempty" eebus:"key"` - SequenceId []PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type HvacSystemFunctionPowerSequenceRelationDataElementsType struct { - SystemFunctionId *ElementTagType `json:"systemFunctionId,omitempty"` - SequenceId *ElementTagType `json:"sequenceId,omitempty"` -} - -type HvacSystemFunctionPowerSequenceRelationListDataType struct { - HvacSystemFunctionPowerSequenceRelationData []HvacSystemFunctionPowerSequenceRelationDataType `json:"hvacSystemFunctionPowerSequenceRelationData,omitempty"` -} - -type HvacSystemFunctionPowerSequenceRelationListDataSelectorsType struct { - SystemFunctionId *HvacSystemFunctionIdType `json:"systemFunctionId,omitempty"` -} - -type HvacSystemFunctionDescriptionDataType struct { - SystemFunctionId *HvacSystemFunctionIdType `json:"systemFunctionId,omitempty" eebus:"key"` - SystemFunctionType *HvacSystemFunctionTypeType `json:"systemFunctionType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type HvacSystemFunctionDescriptionDataElementsType struct { - SystemFunctionId *ElementTagType `json:"systemFunctionId,omitempty"` - SystemFunctionType *ElementTagType `json:"systemFunctionType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type HvacSystemFunctionDescriptionListDataType struct { - HvacSystemFunctionDescriptionData []HvacSystemFunctionDescriptionDataType `json:"hvacSystemFunctionDescriptionData,omitempty"` -} - -type HvacSystemFunctionDescriptionListDataSelectorsType struct { - SystemFunctionId *HvacSystemFunctionIdType `json:"systemFunctionId,omitempty"` -} - -type HvacOperationModeDescriptionDataType struct { - OperationModeId *HvacOperationModeIdType `json:"operationModeId,omitempty" eebus:"key"` - OperationModeType *HvacOperationModeTypeType `json:"operationModeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type HvacOperationModeDescriptionDataElementsType struct { - OperationModeId *ElementTagType `json:"operationModeId,omitempty"` - OperationModeType *ElementTagType `json:"operationModeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type HvacOperationModeDescriptionListDataType struct { - HvacOperationModeDescriptionData []HvacOperationModeDescriptionDataType `json:"hvacOperationModeDescriptionData,omitempty"` -} - -type HvacOperationModeDescriptionListDataSelectorsType struct { - OperationModeId *HvacOperationModeIdType `json:"operationModeId,omitempty"` -} - -type HvacOverrunDataType struct { - OverrunId *HvacOverrunIdType `json:"overrunId,omitempty" eebus:"key"` - OverrunStatus *HvacOverrunStatusType `json:"overrunStatus,omitempty"` - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty"` - IsOverrunStatusChangeable *bool `json:"isOverrunStatusChangeable,omitempty"` -} - -type HvacOverrunDataElementsType struct { - OverrunId *ElementTagType `json:"overrunId,omitempty"` - OverrunStatus *ElementTagType `json:"overrunStatus,omitempty"` - TimeTableId *ElementTagType `json:"timeTableId,omitempty"` - IsOverrunStatusChangeable *ElementTagType `json:"isOverrunStatusChangeable,omitempty"` -} - -type HvacOverrunListDataType struct { - HvacOverrunData []HvacOverrunDataType `json:"hvacOverrunData,omitempty"` -} - -type HvacOverrunListDataSelectorsType struct { - OverrunId *HvacOverrunIdType `json:"overrunId,omitempty"` -} - -type HvacOverrunDescriptionDataType struct { - OverrunId *HvacOverrunIdType `json:"overrunId,omitempty" eebus:"key"` - OverrunType *HvacOverrunTypeType `json:"overrunType,omitempty"` - AffectedSystemFunctionId []HvacSystemFunctionIdType `json:"affectedSystemFunctionId,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type HvacOverrunDescriptionDataElementsType struct { - OverrunId *ElementTagType `json:"overrunId,omitempty"` - OverrunType *ElementTagType `json:"overrunType,omitempty"` - AffectedSystemFunctionId *ElementTagType `json:"affectedSystemFunctionId,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type HvacOverrunDescriptionListDataType struct { - HvacOverrunDescriptionData []HvacOverrunDescriptionDataType `json:"hvacOverrunDescriptionData,omitempty"` -} - -type HvacOverrunDescriptionListDataSelectorsType struct { - OverrunId *HvacOverrunIdType `json:"overrunId,omitempty"` -} diff --git a/spine/model/hvac_additions.go b/spine/model/hvac_additions.go deleted file mode 100644 index a62824b5..00000000 --- a/spine/model/hvac_additions.go +++ /dev/null @@ -1,105 +0,0 @@ -package model - -// HvacSystemFunctionListDataType - -var _ Updater = (*HvacSystemFunctionListDataType)(nil) - -func (r *HvacSystemFunctionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []HvacSystemFunctionDataType - if newList != nil { - newData = newList.(*HvacSystemFunctionListDataType).HvacSystemFunctionData - } - - r.HvacSystemFunctionData = UpdateList(r.HvacSystemFunctionData, newData, filterPartial, filterDelete) -} - -// HvacSystemFunctionOperationModeRelationListDataType - -var _ Updater = (*HvacSystemFunctionOperationModeRelationListDataType)(nil) - -func (r *HvacSystemFunctionOperationModeRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []HvacSystemFunctionOperationModeRelationDataType - if newList != nil { - newData = newList.(*HvacSystemFunctionOperationModeRelationListDataType).HvacSystemFunctionOperationModeRelationData - } - - r.HvacSystemFunctionOperationModeRelationData = UpdateList(r.HvacSystemFunctionOperationModeRelationData, newData, filterPartial, filterDelete) -} - -// HvacSystemFunctionSetpointRelationListDataType - -var _ Updater = (*HvacSystemFunctionSetpointRelationListDataType)(nil) - -func (r *HvacSystemFunctionSetpointRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []HvacSystemFunctionSetpointRelationDataType - if newList != nil { - newData = newList.(*HvacSystemFunctionSetpointRelationListDataType).HvacSystemFunctionSetpointRelationData - } - - r.HvacSystemFunctionSetpointRelationData = UpdateList(r.HvacSystemFunctionSetpointRelationData, newData, filterPartial, filterDelete) -} - -// HvacSystemFunctionPowerSequenceRelationListDataType - -var _ Updater = (*HvacSystemFunctionPowerSequenceRelationListDataType)(nil) - -func (r *HvacSystemFunctionPowerSequenceRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []HvacSystemFunctionPowerSequenceRelationDataType - if newList != nil { - newData = newList.(*HvacSystemFunctionPowerSequenceRelationListDataType).HvacSystemFunctionPowerSequenceRelationData - } - - r.HvacSystemFunctionPowerSequenceRelationData = UpdateList(r.HvacSystemFunctionPowerSequenceRelationData, newData, filterPartial, filterDelete) -} - -// HvacSystemFunctionDescriptionListDataType - -var _ Updater = (*HvacSystemFunctionDescriptionListDataType)(nil) - -func (r *HvacSystemFunctionDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []HvacSystemFunctionDescriptionDataType - if newList != nil { - newData = newList.(*HvacSystemFunctionDescriptionListDataType).HvacSystemFunctionDescriptionData - } - - r.HvacSystemFunctionDescriptionData = UpdateList(r.HvacSystemFunctionDescriptionData, newData, filterPartial, filterDelete) -} - -// HvacOperationModeDescriptionListDataType - -var _ Updater = (*HvacOperationModeDescriptionListDataType)(nil) - -func (r *HvacOperationModeDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []HvacOperationModeDescriptionDataType - if newList != nil { - newData = newList.(*HvacOperationModeDescriptionListDataType).HvacOperationModeDescriptionData - } - - r.HvacOperationModeDescriptionData = UpdateList(r.HvacOperationModeDescriptionData, newData, filterPartial, filterDelete) -} - -// HvacOverrunListDataType - -var _ Updater = (*HvacOverrunListDataType)(nil) - -func (r *HvacOverrunListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []HvacOverrunDataType - if newList != nil { - newData = newList.(*HvacOverrunListDataType).HvacOverrunData - } - - r.HvacOverrunData = UpdateList(r.HvacOverrunData, newData, filterPartial, filterDelete) -} - -// HvacOverrunDescriptionListDataType - -var _ Updater = (*HvacOverrunDescriptionListDataType)(nil) - -func (r *HvacOverrunDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []HvacOverrunDescriptionDataType - if newList != nil { - newData = newList.(*HvacOverrunDescriptionListDataType).HvacOverrunDescriptionData - } - - r.HvacOverrunDescriptionData = UpdateList(r.HvacOverrunDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/hvac_additions_test.go b/spine/model/hvac_additions_test.go deleted file mode 100644 index 13622b38..00000000 --- a/spine/model/hvac_additions_test.go +++ /dev/null @@ -1,312 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestHvacSystemFunctionListDataType_Update(t *testing.T) { - sut := HvacSystemFunctionListDataType{ - HvacSystemFunctionData: []HvacSystemFunctionDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), - IsOverrunActive: util.Ptr(false), - }, - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - IsOverrunActive: util.Ptr(false), - }, - }, - } - - newData := HvacSystemFunctionListDataType{ - HvacSystemFunctionData: []HvacSystemFunctionDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - IsOverrunActive: util.Ptr(true), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.HvacSystemFunctionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SystemFunctionId)) - assert.Equal(t, false, *item1.IsOverrunActive) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SystemFunctionId)) - assert.Equal(t, true, *item2.IsOverrunActive) -} - -func TestHvacSystemFunctionOperationModeRelationListDataType_Update(t *testing.T) { - sut := HvacSystemFunctionOperationModeRelationListDataType{ - HvacSystemFunctionOperationModeRelationData: []HvacSystemFunctionOperationModeRelationDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), - OperationModeId: util.Ptr(HvacOperationModeIdType(0)), - }, - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - OperationModeId: util.Ptr(HvacOperationModeIdType(0)), - }, - }, - } - - newData := HvacSystemFunctionOperationModeRelationListDataType{ - HvacSystemFunctionOperationModeRelationData: []HvacSystemFunctionOperationModeRelationDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - OperationModeId: util.Ptr(HvacOperationModeIdType(1)), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.HvacSystemFunctionOperationModeRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SystemFunctionId)) - assert.Equal(t, 0, int(*item1.OperationModeId)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SystemFunctionId)) - assert.Equal(t, 1, int(*item2.OperationModeId)) -} - -func TestHvacSystemFunctionSetpointRelationListDataType_Update(t *testing.T) { - sut := HvacSystemFunctionSetpointRelationListDataType{ - HvacSystemFunctionSetpointRelationData: []HvacSystemFunctionSetpointRelationDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), - OperationModeId: util.Ptr(HvacOperationModeIdType(0)), - }, - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - OperationModeId: util.Ptr(HvacOperationModeIdType(0)), - }, - }, - } - - newData := HvacSystemFunctionSetpointRelationListDataType{ - HvacSystemFunctionSetpointRelationData: []HvacSystemFunctionSetpointRelationDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - OperationModeId: util.Ptr(HvacOperationModeIdType(1)), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.HvacSystemFunctionSetpointRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SystemFunctionId)) - assert.Equal(t, 0, int(*item1.OperationModeId)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SystemFunctionId)) - assert.Equal(t, 1, int(*item2.OperationModeId)) -} - -func TestHvacSystemFunctionPowerSequenceRelationListDataType_Update(t *testing.T) { - sut := HvacSystemFunctionPowerSequenceRelationListDataType{ - HvacSystemFunctionPowerSequenceRelationData: []HvacSystemFunctionPowerSequenceRelationDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), - SequenceId: []PowerSequenceIdType{0}, - }, - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - SequenceId: []PowerSequenceIdType{0}, - }, - }, - } - - newData := HvacSystemFunctionPowerSequenceRelationListDataType{ - HvacSystemFunctionPowerSequenceRelationData: []HvacSystemFunctionPowerSequenceRelationDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - SequenceId: []PowerSequenceIdType{1}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.HvacSystemFunctionPowerSequenceRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SystemFunctionId)) - assert.Equal(t, 0, int(item1.SequenceId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SystemFunctionId)) - assert.Equal(t, 1, int(item2.SequenceId[0])) -} - -func TestHvacSystemFunctionDescriptionListDataType_Update(t *testing.T) { - sut := HvacSystemFunctionDescriptionListDataType{ - HvacSystemFunctionDescriptionData: []HvacSystemFunctionDescriptionDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := HvacSystemFunctionDescriptionListDataType{ - HvacSystemFunctionDescriptionData: []HvacSystemFunctionDescriptionDataType{ - { - SystemFunctionId: util.Ptr(HvacSystemFunctionIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.HvacSystemFunctionDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SystemFunctionId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SystemFunctionId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestHvacOperationModeDescriptionListDataType_Update(t *testing.T) { - sut := HvacOperationModeDescriptionListDataType{ - HvacOperationModeDescriptionData: []HvacOperationModeDescriptionDataType{ - { - OperationModeId: util.Ptr(HvacOperationModeIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - OperationModeId: util.Ptr(HvacOperationModeIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := HvacOperationModeDescriptionListDataType{ - HvacOperationModeDescriptionData: []HvacOperationModeDescriptionDataType{ - { - OperationModeId: util.Ptr(HvacOperationModeIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.HvacOperationModeDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.OperationModeId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.OperationModeId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestHvacOverrunListDataType_Update(t *testing.T) { - sut := HvacOverrunListDataType{ - HvacOverrunData: []HvacOverrunDataType{ - { - OverrunId: util.Ptr(HvacOverrunIdType(0)), - IsOverrunStatusChangeable: util.Ptr(false), - }, - { - OverrunId: util.Ptr(HvacOverrunIdType(1)), - IsOverrunStatusChangeable: util.Ptr(false), - }, - }, - } - - newData := HvacOverrunListDataType{ - HvacOverrunData: []HvacOverrunDataType{ - { - OverrunId: util.Ptr(HvacOverrunIdType(1)), - IsOverrunStatusChangeable: util.Ptr(true), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.HvacOverrunData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.OverrunId)) - assert.Equal(t, false, *item1.IsOverrunStatusChangeable) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.OverrunId)) - assert.Equal(t, true, *item2.IsOverrunStatusChangeable) -} - -func TestHvacOverrunDescriptionListDataType_Update(t *testing.T) { - sut := HvacOverrunDescriptionListDataType{ - HvacOverrunDescriptionData: []HvacOverrunDescriptionDataType{ - { - OverrunId: util.Ptr(HvacOverrunIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - OverrunId: util.Ptr(HvacOverrunIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := HvacOverrunDescriptionListDataType{ - HvacOverrunDescriptionData: []HvacOverrunDescriptionDataType{ - { - OverrunId: util.Ptr(HvacOverrunIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.HvacOverrunDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.OverrunId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.OverrunId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/identification.go b/spine/model/identification.go deleted file mode 100644 index 6e843293..00000000 --- a/spine/model/identification.go +++ /dev/null @@ -1,82 +0,0 @@ -package model - -type IdentificationIdType uint - -type IdentificationTypeType string - -const ( - IdentificationTypeTypeEui48 IdentificationTypeType = "eui48" - IdentificationTypeTypeEui64 IdentificationTypeType = "eui64" - IdentificationTypeTypeUserrfidtag IdentificationTypeType = "userRfidTag" -) - -type IdentificationValueType string - -type SessionIdType uint - -type IdentificationDataType struct { - IdentificationId *IdentificationIdType `json:"identificationId,omitempty" eebus:"key"` - IdentificationType *IdentificationTypeType `json:"identificationType,omitempty"` - IdentificationValue *IdentificationValueType `json:"identificationValue,omitempty"` - Authorized *bool `json:"authorized,omitempty"` -} - -type IdentificationDataElementsType struct { - IdentificationId *ElementTagType `json:"identificationId,omitempty"` - IdentificationType *ElementTagType `json:"identificationType,omitempty"` - IdentificationValue *ElementTagType `json:"identificationValue,omitempty"` - Authorized *ElementTagType `json:"authorized,omitempty"` -} - -type IdentificationListDataType struct { - IdentificationData []IdentificationDataType `json:"identificationData,omitempty"` -} - -type IdentificationListDataSelectorsType struct { - IdentificationId *IdentificationIdType `json:"identificationId,omitempty"` - IdentificationType *IdentificationTypeType `json:"identificationType,omitempty"` -} - -type SessionIdentificationDataType struct { - SessionId *SessionIdType `json:"sessionId,omitempty" eebus:"key"` - IdentificationId *IdentificationIdType `json:"identificationId,omitempty"` - IsLatestSession *bool `json:"isLatestSession,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` -} - -type SessionIdentificationDataElementsType struct { - SessionId *ElementTagType `json:"sessionId,omitempty"` - IdentificationId *ElementTagType `json:"identificationId,omitempty"` - IsLatestSession *ElementTagType `json:"isLatestSession,omitempty"` - TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` -} - -type SessionIdentificationListDataType struct { - SessionIdentificationData []SessionIdentificationDataType `json:"sessionIdentificationData,omitempty"` -} - -type SessionIdentificationListDataSelectorsType struct { - SessionId *SessionIdType `json:"sessionId,omitempty"` - IdentificationId *IdentificationIdType `json:"identificationId,omitempty"` - IsLatestSession *bool `json:"isLatestSession,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` -} - -type SessionMeasurementRelationDataType struct { - SessionId *SessionIdType `json:"sessionId,omitempty" eebus:"key"` - MeasurementId []MeasurementIdType `json:"measurementId,omitempty"` -} - -type SessionMeasurementRelationDataElementsType struct { - SessionId *ElementTagType `json:"sessionId,omitempty"` - MeasurementId *ElementTagType `json:"measurementId,omitempty"` -} - -type SessionMeasurementRelationListDataType struct { - SessionMeasurementRelationData []SessionMeasurementRelationDataType `json:"sessionMeasurementRelationData,omitempty"` -} - -type SessionMeasurementRelationListDataSelectorsType struct { - SessionId *SessionIdType `json:"sessionId,omitempty"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` -} diff --git a/spine/model/identification_additions.go b/spine/model/identification_additions.go deleted file mode 100644 index d2ee37c0..00000000 --- a/spine/model/identification_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// IdentificationListDataType - -var _ Updater = (*IdentificationListDataType)(nil) - -func (r *IdentificationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []IdentificationDataType - if newList != nil { - newData = newList.(*IdentificationListDataType).IdentificationData - } - - r.IdentificationData = UpdateList(r.IdentificationData, newData, filterPartial, filterDelete) -} - -// SessionIdentificationListDataType - -var _ Updater = (*SessionIdentificationListDataType)(nil) - -func (r *SessionIdentificationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SessionIdentificationDataType - if newList != nil { - newData = newList.(*SessionIdentificationListDataType).SessionIdentificationData - } - - r.SessionIdentificationData = UpdateList(r.SessionIdentificationData, newData, filterPartial, filterDelete) -} - -// SessionMeasurementRelationListDataType - -var _ Updater = (*SessionMeasurementRelationListDataType)(nil) - -func (r *SessionMeasurementRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SessionMeasurementRelationDataType - if newList != nil { - newData = newList.(*SessionMeasurementRelationListDataType).SessionMeasurementRelationData - } - - r.SessionMeasurementRelationData = UpdateList(r.SessionMeasurementRelationData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/identification_additions_test.go b/spine/model/identification_additions_test.go deleted file mode 100644 index ba843cf5..00000000 --- a/spine/model/identification_additions_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestIdentificationListDataType_Update(t *testing.T) { - sut := IdentificationListDataType{ - IdentificationData: []IdentificationDataType{ - { - IdentificationId: util.Ptr(IdentificationIdType(0)), - IdentificationType: util.Ptr(IdentificationTypeTypeEui48), - }, - { - IdentificationId: util.Ptr(IdentificationIdType(1)), - IdentificationType: util.Ptr(IdentificationTypeTypeEui48), - }, - }, - } - - newData := IdentificationListDataType{ - IdentificationData: []IdentificationDataType{ - { - IdentificationId: util.Ptr(IdentificationIdType(1)), - IdentificationType: util.Ptr(IdentificationTypeTypeEui64), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.IdentificationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.IdentificationId)) - assert.Equal(t, IdentificationTypeTypeEui48, *item1.IdentificationType) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.IdentificationId)) - assert.Equal(t, IdentificationTypeTypeEui64, *item2.IdentificationType) -} diff --git a/spine/model/incentivetable.go b/spine/model/incentivetable.go deleted file mode 100644 index c5cacb0d..00000000 --- a/spine/model/incentivetable.go +++ /dev/null @@ -1,103 +0,0 @@ -package model - -type IncentiveTableType struct { - Tariff *TariffDataType `json:"tariff,omitempty"` // ignoring changes - IncentiveSlot []IncentiveTableIncentiveSlotType `json:"incentiveSlot,omitempty"` -} - -type IncentiveTableElementsType struct { - Tariff *TariffDataElementsType `json:"tariff,omitempty"` // ignoring changes - IncentiveSlot *IncentiveTableIncentiveSlotElementsType `json:"incentiveSlot,omitempty"` -} - -type IncentiveTableIncentiveSlotType struct { - TimeInterval *TimeTableDataType `json:"timeInterval,omitempty"` // ignoring changes - Tier []IncentiveTableTierType `json:"tier,omitempty"` -} - -type IncentiveTableIncentiveSlotElementsType struct { - TimeInterval *TimeTableDataElementsType `json:"timeInterval,omitempty"` // ignoring changes - Tier *IncentiveTableTierElementsType `json:"tier,omitempty"` -} - -type IncentiveTableTierType struct { - Tier *TierDataType `json:"tier,omitempty"` // ignoring changes - Boundary []TierBoundaryDataType `json:"boundary,omitempty"` // ignoring changes - Incentive []IncentiveDataType `json:"incentive,omitempty"` // ignoring changes -} - -type IncentiveTableTierElementsType struct { - Tier *TierDataElementsType `json:"tier,omitempty"` // ignoring changes - Boundary *TierBoundaryDataElementsType `json:"boundary,omitempty"` // ignoring changes - Incentive *IncentiveDataElementsType `json:"incentive,omitempty"` // ignoring changes -} - -type IncentiveTableDataType struct { - IncentiveTable []IncentiveTableType `json:"incentiveTable,omitempty"` -} - -type IncentiveTableDataElementsType struct { - IncentiveTable *IncentiveTableElementsType `json:"incentiveTable,omitempty"` -} - -type IncentiveTableDataSelectorsType struct { - Tariff *TariffListDataSelectorsType `json:"tariff,omitempty"` -} - -type IncentiveTableDescriptionType struct { - TariffDescription *TariffDescriptionDataType `json:"tariffDescription,omitempty"` - Tier []IncentiveTableDescriptionTierType `json:"tier,omitempty"` -} - -type IncentiveTableDescriptionElementsType struct { - TariffDescription *TariffDescriptionDataElementsType `json:"tariffDescription,omitempty"` - Tier *IncentiveTableDescriptionTierType `json:"tier,omitempty"` -} - -type IncentiveTableDescriptionTierType struct { - TierDescription *TierDescriptionDataType `json:"tierDescription,omitempty"` - BoundaryDescription []TierBoundaryDescriptionDataType `json:"boundaryDescription,omitempty"` - IncentiveDescription []IncentiveDescriptionDataType `json:"incentiveDescription,omitempty"` -} - -type IncentiveTableDescriptionTierElementsType struct { - TierDescription *TierDescriptionDataElementsType `json:"tierDescription,omitempty"` - BoundaryDescription *TierBoundaryDescriptionDataElementsType `json:"boundaryDescription,omitempty"` - IncentiveDescription *IncentiveDescriptionDataElementsType `json:"incentiveDescription,omitempty"` -} - -type IncentiveTableDescriptionDataType struct { - IncentiveTableDescription []IncentiveTableDescriptionType `json:"incentiveTableDescription,omitempty"` -} - -type IncentiveTableDescriptionDataElementsType struct { - IncentiveTableDescription *IncentiveTableDescriptionElementsType `json:"incentiveTableDescription,omitempty"` -} - -type IncentiveTableDescriptionDataSelectorsType struct { - TariffDescription *TariffDescriptionListDataSelectorsType `json:"tariffDescription,omitempty"` -} - -type IncentiveTableConstraintsType struct { - Tariff *TariffDataType `json:"tariff,omitempty"` - TariffConstraints *TariffOverallConstraintsDataType `json:"tariffConstraints,omitempty"` - IncentiveSlotConstraints *TimeTableConstraintsDataType `json:"incentiveSlotConstraints,omitempty"` -} - -type IncentiveTableConstraintsElementsType struct { - Tariff *TariffDataElementsType `json:"tariff,omitempty"` - TariffConstraints *TariffOverallConstraintsDataElementsType `json:"tariffConstraints,omitempty"` - IncentiveSlotConstraints *TimeTableConstraintsDataElementsType `json:"incentiveSlotConstraints,omitempty"` -} - -type IncentiveTableConstraintsDataType struct { - IncentiveTableConstraints []IncentiveTableConstraintsType `json:"incentiveTableConstraints,omitempty"` -} - -type IncentiveTableConstraintsDataElementsType struct { - IncentiveTableConstraints *IncentiveTableConstraintsElementsType `json:"incentiveTableConstraints,omitempty"` -} - -type IncentiveTableConstraintsDataSelectorsType struct { - Tariff *TariffListDataSelectorsType `json:"tariff,omitempty"` -} diff --git a/spine/model/loadcontrol.go b/spine/model/loadcontrol.go deleted file mode 100644 index a9d0b8fa..00000000 --- a/spine/model/loadcontrol.go +++ /dev/null @@ -1,185 +0,0 @@ -package model - -type LoadControlEventIdType uint - -type LoadControlEventActionType string - -const ( - LoadControlEventActionTypePause LoadControlEventActionType = "pause" - LoadControlEventActionTypeResume LoadControlEventActionType = "resume" - LoadControlEventActionTypeReduce LoadControlEventActionType = "reduce" - LoadControlEventActionTypeIncrease LoadControlEventActionType = "increase" - LoadControlEventActionTypeEmergency LoadControlEventActionType = "emergency" - LoadControlEventActionTypeNormal LoadControlEventActionType = "normal" -) - -type LoadControlEventStateType string - -const ( - LoadControlEventStateTypeEventAccepted LoadControlEventStateType = "eventAccepted" - LoadControlEventStateTypeEventStarted LoadControlEventStateType = "eventStarted" - LoadControlEventStateTypeEventStopped LoadControlEventStateType = "eventStopped" - LoadControlEventStateTypeEventRejected LoadControlEventStateType = "eventRejected" - LoadControlEventStateTypeEventCancelled LoadControlEventStateType = "eventCancelled" - LoadControlEventStateTypeEventError LoadControlEventStateType = "eventError" -) - -type LoadControlLimitIdType uint - -type LoadControlLimitTypeType string - -const ( - LoadControlLimitTypeTypeMinValueLimit LoadControlLimitTypeType = "minValueLimit" - LoadControlLimitTypeTypeMaxValueLimit LoadControlLimitTypeType = "maxValueLimit" - LoadControlLimitTypeTypeSignDependentAbsValueLimit LoadControlLimitTypeType = "signDependentAbsValueLimit" -) - -type LoadControlCategoryType string - -const ( - LoadControlCategoryTypeObligation LoadControlCategoryType = "obligation" - LoadControlCategoryTypeRecommendation LoadControlCategoryType = "recommendation" - LoadControlCategoryTypeOptimization LoadControlCategoryType = "optimization" -) - -type LoadControlNodeDataType struct { - IsNodeRemoteControllable *bool `json:"isNodeRemoteControllable,omitempty"` -} - -type LoadControlNodeDataElementsType struct { - IsNodeRemoteControllable *ElementTagType `json:"isNodeRemoteControllable,omitempty"` -} - -type LoadControlEventDataType struct { - Timestamp *string `json:"timestamp,omitempty"` - EventId *LoadControlEventIdType `json:"eventId,omitempty" eebus:"key"` - EventActionConsume *LoadControlEventActionType `json:"eventActionConsume,omitempty"` - EventActionProduce *LoadControlEventActionType `json:"eventActionProduce,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` -} - -type LoadControlEventDataElementsType struct { - Timestamp *ElementTagType `json:"timestamp,omitempty"` - EventId *ElementTagType `json:"eventId,omitempty"` - EventActionConsume *ElementTagType `json:"eventActionConsume,omitempty"` - EventActionProduce *ElementTagType `json:"eventActionProduce,omitempty"` - TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` -} - -type LoadControlEventListDataType struct { - LoadControlEventData []LoadControlEventDataType `json:"loadControlEventData,omitempty"` -} - -type LoadControlEventListDataSelectorsType struct { - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` - EventId *LoadControlEventIdType `json:"eventId,omitempty"` -} - -type LoadControlStateDataType struct { - Timestamp *string `json:"timestamp"` - EventId *LoadControlEventIdType `json:"eventId,omitempty" eebus:"key"` - EventStateConsume *LoadControlEventStateType `json:"eventStateConsume"` - AppliedEventActionConsume *LoadControlEventActionType `json:"appliedEventActionConsume"` - EventStateProduce *LoadControlEventStateType `json:"eventStateProduce"` - AppliedEventActionProduce *LoadControlEventActionType `json:"appliedEventActionProduce"` -} - -type LoadControlStateDataElementsType struct { - Timestamp *ElementTagType `json:"timestamp"` - EventId *ElementTagType `json:"eventId,omitempty"` - EventStateConsume *ElementTagType `json:"eventStateConsume"` - AppliedEventActionConsume *ElementTagType `json:"appliedEventActionConsume"` - EventStateProduce *ElementTagType `json:"eventStateProduce"` - AppliedEventActionProduce *ElementTagType `json:"appliedEventActionProduce"` -} - -type LoadControlStateListDataType struct { - LoadControlStateData []LoadControlStateDataType `json:"loadControlStateData,omitempty"` -} - -type LoadControlStateListDataSelectorsType struct { - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` - EventId *LoadControlEventIdType `json:"eventId,omitempty"` -} - -type LoadControlLimitDataType struct { - LimitId *LoadControlLimitIdType `json:"limitId,omitempty" eebus:"key"` - IsLimitChangeable *bool `json:"isLimitChangeable,omitempty"` - IsLimitActive *bool `json:"isLimitActive,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` -} - -type LoadControlLimitDataElementsType struct { - LimitId *ElementTagType `json:"limitId,omitempty"` - IsLimitChangeable *ElementTagType `json:"isLimitChangeable,omitempty"` - IsLimitActive *ElementTagType `json:"isLimitActive,omitempty"` - TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` - Value *ScaledNumberElementsType `json:"value,omitempty"` -} - -type LoadControlLimitListDataType struct { - LoadControlLimitData []LoadControlLimitDataType `json:"loadControlLimitData,omitempty"` -} - -type LoadControlLimitListDataSelectorsType struct { - LimitId *LoadControlLimitIdType `json:"limitId,omitempty"` -} - -type LoadControlLimitConstraintsDataType struct { - LimitId *LoadControlLimitIdType `json:"limitId,omitempty" eebus:"key"` - ValueRangeMin *ScaledNumberType `json:"valueRangeMin,omitempty"` - ValueRangeMax *ScaledNumberType `json:"valueRangeMax,omitempty"` - ValueStepSize *ScaledNumberType `json:"valueStepSize,omitempty"` -} - -type LoadControlLimitConstraintsDataElementsType struct { - LimitId *ElementTagType `json:"limitId,omitempty"` - ValueRangeMin *ScaledNumberElementsType `json:"valueRangeMin,omitempty"` - ValueRangeMax *ScaledNumberElementsType `json:"valueRangeMax,omitempty"` - ValueStepSize *ScaledNumberElementsType `json:"valueStepSize,omitempty"` -} - -type LoadControlLimitConstraintsListDataType struct { - LoadControlLimitConstraintsData []LoadControlLimitConstraintsDataType `json:"loadControlLimitConstraintsData,omitempty"` -} - -type LoadControlLimitConstraintsListDataSelectorsType struct { - LimitId *LoadControlLimitIdType `json:"limitId,omitempty"` -} - -type LoadControlLimitDescriptionDataType struct { - LimitId *LoadControlLimitIdType `json:"limitId,omitempty" eebus:"key"` - LimitType *LoadControlLimitTypeType `json:"limitType,omitempty"` - LimitCategory *LoadControlCategoryType `json:"limitCategory,omitempty"` - LimitDirection *EnergyDirectionType `json:"limitDirection,omitempty"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type LoadControlLimitDescriptionDataElementsType struct { - LimitId *ElementTagType `json:"limitId,omitempty"` - LimitType *ElementTagType `json:"limitType,omitempty"` - LimitCategory *ElementTagType `json:"limitCategory,omitempty"` - LimitDirection *ElementTagType `json:"limitDirection,omitempty"` - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type LoadControlLimitDescriptionListDataType struct { - LoadControlLimitDescriptionData []LoadControlLimitDescriptionDataType `json:"loadControlLimitDescriptionData,omitempty"` -} - -type LoadControlLimitDescriptionListDataSelectorsType struct { - LimitId *LoadControlLimitIdType `json:"limitId,omitempty"` - LimitType *LoadControlLimitTypeType `json:"limitType,omitempty"` - LimitDirection *EnergyDirectionType `json:"limitDirection,omitempty"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} diff --git a/spine/model/loadcontrol_additions.go b/spine/model/loadcontrol_additions.go deleted file mode 100644 index 4b05559f..00000000 --- a/spine/model/loadcontrol_additions.go +++ /dev/null @@ -1,66 +0,0 @@ -package model - -// LoadControlEventListDataType - -var _ Updater = (*LoadControlEventListDataType)(nil) - -func (r *LoadControlEventListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []LoadControlEventDataType - if newList != nil { - newData = newList.(*LoadControlEventListDataType).LoadControlEventData - } - - r.LoadControlEventData = UpdateList(r.LoadControlEventData, newData, filterPartial, filterDelete) -} - -// LoadControlStateListDataType - -var _ Updater = (*LoadControlStateListDataType)(nil) - -func (r *LoadControlStateListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []LoadControlStateDataType - if newList != nil { - newData = newList.(*LoadControlStateListDataType).LoadControlStateData - } - - r.LoadControlStateData = UpdateList(r.LoadControlStateData, newData, filterPartial, filterDelete) -} - -// LoadControlLimitListDataType - -var _ Updater = (*LoadControlLimitListDataType)(nil) - -func (r *LoadControlLimitListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []LoadControlLimitDataType - if newList != nil { - newData = newList.(*LoadControlLimitListDataType).LoadControlLimitData - } - - r.LoadControlLimitData = UpdateList(r.LoadControlLimitData, newData, filterPartial, filterDelete) -} - -// LoadControlLimitConstraintsListDataType - -var _ Updater = (*LoadControlLimitConstraintsListDataType)(nil) - -func (r *LoadControlLimitConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []LoadControlLimitConstraintsDataType - if newList != nil { - newData = newList.(*LoadControlLimitConstraintsListDataType).LoadControlLimitConstraintsData - } - - r.LoadControlLimitConstraintsData = UpdateList(r.LoadControlLimitConstraintsData, newData, filterPartial, filterDelete) -} - -// LoadControlLimitDescriptionListDataType - -var _ Updater = (*LoadControlLimitDescriptionListDataType)(nil) - -func (r *LoadControlLimitDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []LoadControlLimitDescriptionDataType - if newList != nil { - newData = newList.(*LoadControlLimitDescriptionListDataType).LoadControlLimitDescriptionData - } - - r.LoadControlLimitDescriptionData = UpdateList(r.LoadControlLimitDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/loadcontrol_additions_test.go b/spine/model/loadcontrol_additions_test.go deleted file mode 100644 index 23f380d8..00000000 --- a/spine/model/loadcontrol_additions_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestLoadControlEventListDataType_Update(t *testing.T) { - sut := LoadControlEventListDataType{ - LoadControlEventData: []LoadControlEventDataType{ - { - EventId: util.Ptr(LoadControlEventIdType(0)), - EventActionConsume: util.Ptr(LoadControlEventActionTypeNormal), - }, - { - EventId: util.Ptr(LoadControlEventIdType(1)), - EventActionConsume: util.Ptr(LoadControlEventActionTypeNormal), - }, - }, - } - - newData := LoadControlEventListDataType{ - LoadControlEventData: []LoadControlEventDataType{ - { - EventId: util.Ptr(LoadControlEventIdType(1)), - EventActionConsume: util.Ptr(LoadControlEventActionTypeIncrease), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.LoadControlEventData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.EventId)) - assert.Equal(t, LoadControlEventActionTypeNormal, *item1.EventActionConsume) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.EventId)) - assert.Equal(t, LoadControlEventActionTypeIncrease, *item2.EventActionConsume) -} - -func TestLoadControlStateListDataType_Update(t *testing.T) { - sut := LoadControlStateListDataType{ - LoadControlStateData: []LoadControlStateDataType{ - { - EventId: util.Ptr(LoadControlEventIdType(0)), - EventStateConsume: util.Ptr(LoadControlEventStateTypeEventAccepted), - }, - { - EventId: util.Ptr(LoadControlEventIdType(1)), - EventStateConsume: util.Ptr(LoadControlEventStateTypeEventAccepted), - }, - }, - } - - newData := LoadControlStateListDataType{ - LoadControlStateData: []LoadControlStateDataType{ - { - EventId: util.Ptr(LoadControlEventIdType(1)), - EventStateConsume: util.Ptr(LoadControlEventStateTypeEventStopped), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.LoadControlStateData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.EventId)) - assert.Equal(t, LoadControlEventStateTypeEventAccepted, *item1.EventStateConsume) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.EventId)) - assert.Equal(t, LoadControlEventStateTypeEventStopped, *item2.EventStateConsume) -} - -func TestLoadControlLimitListDataType_Update(t *testing.T) { - sut := LoadControlLimitListDataType{ - LoadControlLimitData: []LoadControlLimitDataType{ - { - LimitId: util.Ptr(LoadControlLimitIdType(0)), - IsLimitChangeable: util.Ptr(false), - }, - { - LimitId: util.Ptr(LoadControlLimitIdType(1)), - IsLimitChangeable: util.Ptr(false), - }, - }, - } - - newData := LoadControlLimitListDataType{ - LoadControlLimitData: []LoadControlLimitDataType{ - { - LimitId: util.Ptr(LoadControlLimitIdType(1)), - IsLimitChangeable: util.Ptr(true), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.LoadControlLimitData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.LimitId)) - assert.Equal(t, false, *item1.IsLimitChangeable) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.LimitId)) - assert.Equal(t, true, *item2.IsLimitChangeable) -} - -func TestLoadControlLimitConstraintsListDataType_Update(t *testing.T) { - sut := LoadControlLimitConstraintsListDataType{ - LoadControlLimitConstraintsData: []LoadControlLimitConstraintsDataType{ - { - LimitId: util.Ptr(LoadControlLimitIdType(0)), - ValueStepSize: NewScaledNumberType(1), - }, - { - LimitId: util.Ptr(LoadControlLimitIdType(1)), - ValueStepSize: NewScaledNumberType(1), - }, - }, - } - - newData := LoadControlLimitConstraintsListDataType{ - LoadControlLimitConstraintsData: []LoadControlLimitConstraintsDataType{ - { - LimitId: util.Ptr(LoadControlLimitIdType(1)), - ValueStepSize: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.LoadControlLimitConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.LimitId)) - assert.Equal(t, 1.0, float64(item1.ValueStepSize.GetValue())) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.LimitId)) - assert.Equal(t, 10.0, float64(item2.ValueStepSize.GetValue())) -} - -func TestLoadControlLimitDescriptionListDataType_Update(t *testing.T) { - sut := LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: []LoadControlLimitDescriptionDataType{ - { - LimitId: util.Ptr(LoadControlLimitIdType(0)), - LimitCategory: util.Ptr(LoadControlCategoryTypeObligation), - }, - { - LimitId: util.Ptr(LoadControlLimitIdType(1)), - LimitCategory: util.Ptr(LoadControlCategoryTypeObligation), - }, - }, - } - - newData := LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: []LoadControlLimitDescriptionDataType{ - { - LimitId: util.Ptr(LoadControlLimitIdType(1)), - LimitCategory: util.Ptr(LoadControlCategoryTypeOptimization), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.LoadControlLimitDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.LimitId)) - assert.Equal(t, LoadControlCategoryTypeObligation, *item1.LimitCategory) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.LimitId)) - assert.Equal(t, LoadControlCategoryTypeOptimization, *item2.LimitCategory) -} diff --git a/spine/model/measurement.go b/spine/model/measurement.go deleted file mode 100644 index c41e57ef..00000000 --- a/spine/model/measurement.go +++ /dev/null @@ -1,222 +0,0 @@ -package model - -type MeasurementIdType uint - -type MeasurementTypeType string - -const ( - MeasurementTypeTypeAcceleration MeasurementTypeType = "acceleration" - MeasurementTypeTypeAngle MeasurementTypeType = "angle" - MeasurementTypeTypeAngularVelocity MeasurementTypeType = "angularVelocity" - MeasurementTypeTypeArea MeasurementTypeType = "area" - MeasurementTypeTypeAtmosphericPressure MeasurementTypeType = "atmosphericPressure" - MeasurementTypeTypeCapacity MeasurementTypeType = "capacity" - MeasurementTypeTypeConcentration MeasurementTypeType = "concentration" - MeasurementTypeTypeCount MeasurementTypeType = "count" - MeasurementTypeTypeCurrent MeasurementTypeType = "current" - MeasurementTypeTypeDensity MeasurementTypeType = "density" - MeasurementTypeTypeDistance MeasurementTypeType = "distance" - MeasurementTypeTypeElectricField MeasurementTypeType = "electricField" - MeasurementTypeTypeEnergy MeasurementTypeType = "energy" - MeasurementTypeTypeForce MeasurementTypeType = "force" - MeasurementTypeTypeFrequency MeasurementTypeType = "frequency" - MeasurementTypeTypeHarmonicDistortion MeasurementTypeType = "harmonicDistortion" - MeasurementTypeTypeHeat MeasurementTypeType = "heat" - MeasurementTypeTypeHeatFlux MeasurementTypeType = "heatFlux" - MeasurementTypeTypeIlluminance MeasurementTypeType = "illuminance" - MeasurementTypeTypeImpulse MeasurementTypeType = "impulse" - MeasurementTypeTypeLevel MeasurementTypeType = "level" - MeasurementTypeTypeMagneticField MeasurementTypeType = "magneticField" - MeasurementTypeTypeMass MeasurementTypeType = "mass" - MeasurementTypeTypeMassFlow MeasurementTypeType = "massFlow" - MeasurementTypeTypeParticles MeasurementTypeType = "particles" - MeasurementTypeTypePercentage MeasurementTypeType = "percentage" - MeasurementTypeTypePower MeasurementTypeType = "power" - MeasurementTypeTypePowerFactor MeasurementTypeType = "powerFactor" - MeasurementTypeTypePressure MeasurementTypeType = "pressure" - MeasurementTypeTypeRadonActivity MeasurementTypeType = "radonActivity" - MeasurementTypeTypeRelativeHumidity MeasurementTypeType = "relativeHumidity" - MeasurementTypeTypeResistance MeasurementTypeType = "resistance" - MeasurementTypeTypeSolarRadiation MeasurementTypeType = "solarRadiation" - MeasurementTypeTypeSpeed MeasurementTypeType = "speed" - MeasurementTypeTypeTemperature MeasurementTypeType = "temperature" - MeasurementTypeTypeTime MeasurementTypeType = "time" - MeasurementTypeTypeTorque MeasurementTypeType = "torque" - MeasurementTypeTypeUnknown MeasurementTypeType = "unknown" - MeasurementTypeTypeVelocity MeasurementTypeType = "velocity" - MeasurementTypeTypeVoltage MeasurementTypeType = "voltage" - MeasurementTypeTypeVolume MeasurementTypeType = "volume" - MeasurementTypeTypeVolumetricFlow MeasurementTypeType = "volumetricFlow" -) - -type MeasurementValueTypeType string - -const ( - MeasurementValueTypeTypeValue MeasurementValueTypeType = "value" - MeasurementValueTypeTypeAverageValue MeasurementValueTypeType = "averageValue" - MeasurementValueTypeTypeMinvValue MeasurementValueTypeType = "minValue" - MeasurementValueTypeTypeMaxvVlue MeasurementValueTypeType = "maxValue" - MeasurementValueTypeTypeStandardDeviation MeasurementValueTypeType = "standardDeviation" -) - -type MeasurementValueSourceType string - -const ( - MeasurementValueSourceTypeMeasuredValue MeasurementValueSourceType = "measuredValue" - MeasurementValueSourceTypeCalculatedValue MeasurementValueSourceType = "calculatedValue" - MeasurementValueSourceTypeEmpiricalValue MeasurementValueSourceType = "empiricalValue" -) - -type MeasurementValueTendencyType string - -const ( - MeasurementValueTendencyTypeRising MeasurementValueTendencyType = "rising" - MeasurementValueTendencyTypeStable MeasurementValueTendencyType = "stable" - MeasurementValueTendencyTypeFalling MeasurementValueTendencyType = "falling" -) - -type MeasurementValueStateType string - -const ( - MeasurementValueStateTypeNormal MeasurementValueStateType = "normal" - MeasurementValueStateTypeOutofrange MeasurementValueStateType = "outOfRange" - MeasurementValueStateTypeError MeasurementValueStateType = "error" -) - -type MeasurementDataType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty" eebus:"key"` - ValueType *MeasurementValueTypeType `json:"valueType,omitempty" eebus:"key"` - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` - EvaluationPeriod *TimePeriodType `json:"evaluationPeriod,omitempty"` - ValueSource *MeasurementValueSourceType `json:"valueSource,omitempty"` - ValueTendency *MeasurementValueTendencyType `json:"valueTendency,omitempty"` - ValueState *MeasurementValueStateType `json:"valueState,omitempty"` -} - -type MeasurementDataElementsType struct { - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - ValueType *ElementTagType `json:"valueType,omitempty"` - Timestamp *ElementTagType `json:"timestamp,omitempty"` - Value *ElementTagType `json:"value,omitempty"` - EvaluationPeriod *ElementTagType `json:"evaluationPeriod,omitempty"` - ValueSource *ElementTagType `json:"valueSource,omitempty"` - ValueTendency *ElementTagType `json:"valueTendency,omitempty"` - ValueState *ElementTagType `json:"valueState,omitempty"` -} - -type MeasurementListDataType struct { - MeasurementData []MeasurementDataType `json:"measurementData,omitempty"` -} - -type MeasurementListDataSelectorsType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - ValueType *MeasurementValueTypeType `json:"valueType,omitempty"` - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` -} - -type MeasurementSeriesDataType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty" eebus:"key"` - ValueType *MeasurementValueTypeType `json:"valueType,omitempty" eebus:"key"` - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` - EvaluationPeriod *TimePeriodType `json:"evaluationPeriod,omitempty"` - ValueSource *MeasurementValueSourceType `json:"valueSource,omitempty"` - ValueTendency *MeasurementValueTendencyType `json:"valueTendency,omitempty"` - ValueState *MeasurementValueStateType `json:"valueState,omitempty"` -} - -type MeasurementSeriesDataElementsType struct { - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - ValueType *ElementTagType `json:"valueType,omitempty"` - Timestamp *ElementTagType `json:"timestamp,omitempty"` - Value *ElementTagType `json:"value,omitempty"` - EvaluationPeriod *ElementTagType `json:"evaluationPeriod,omitempty"` - ValueSource *ElementTagType `json:"valueSource,omitempty"` - ValueTendency *ElementTagType `json:"valueTendency,omitempty"` - ValueState *ElementTagType `json:"valueState,omitempty"` -} - -type MeasurementSeriesListDataType struct { - MeasurementSeriesData []MeasurementSeriesDataType `json:"measurementSeriesData,omitempty"` -} - -type MeasurementSeriesListDataSelectorsType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - ValueType *MeasurementValueTypeType `json:"valueType,omitempty"` - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` -} - -type MeasurementConstraintsDataType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty" eebus:"key"` - ValueRangeMin *ScaledNumberType `json:"valueRangeMin,omitempty"` - ValueRangeMax *ScaledNumberType `json:"valueRangeMax,omitempty"` - ValueStepSize *ScaledNumberType `json:"valueStepSize,omitempty"` -} - -type MeasurementConstraintsDataElementsType struct { - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - ValueRangeMin *ScaledNumberElementsType `json:"valueRangeMin,omitempty"` - ValueRangeMax *ScaledNumberElementsType `json:"valueRangeMax,omitempty"` - ValueStepSize *ScaledNumberElementsType `json:"valueStepSize,omitempty"` -} - -type MeasurementConstraintsListDataType struct { - MeasurementConstraintsData []MeasurementConstraintsDataType `json:"measurementConstraintsData,omitempty"` -} - -type MeasurementConstraintsListDataSelectorsType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` -} - -type MeasurementDescriptionDataType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty" eebus:"key"` - MeasurementType *MeasurementTypeType `json:"measurementType,omitempty"` - CommodityType *CommodityTypeType `json:"commodityType,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - CalibrationValue *ScaledNumberType `json:"calibrationValue,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type MeasurementDescriptionDataElementsType struct { - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - MeasurementType *ElementTagType `json:"measurementType,omitempty"` - CommodityType *ElementTagType `json:"commodityType,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - CalibrationValue *ScaledNumberElementsType `json:"calibrationValue,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type MeasurementDescriptionListDataType struct { - MeasurementDescriptionData []MeasurementDescriptionDataType `json:"measurementDescriptionData,omitempty"` -} - -type MeasurementDescriptionListDataSelectorsType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - MeasurementType *MeasurementTypeType `json:"measurementType,omitempty"` - CommodityType *CommodityTypeType `json:"commodityType,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} - -type MeasurementThresholdRelationDataType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty" eebus:"key"` - ThresholdId []ThresholdIdType `json:"thresholdId,omitempty"` -} - -type MeasurementThresholdRelationDataElementsType struct { - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - ThresholdId *ElementTagType `json:"thresholdId,omitempty"` -} - -type MeasurementThresholdRelationListDataType struct { - MeasurementThresholdRelationData []MeasurementThresholdRelationDataType `json:"measurementThresholdRelationData,omitempty"` -} - -type MeasurementThresholdRelationListDataSelectorsType struct { - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty"` -} diff --git a/spine/model/measurement_additions.go b/spine/model/measurement_additions.go deleted file mode 100644 index 87cb9055..00000000 --- a/spine/model/measurement_additions.go +++ /dev/null @@ -1,66 +0,0 @@ -package model - -// MeasurementListDataType - -var _ Updater = (*MeasurementListDataType)(nil) - -func (r *MeasurementListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []MeasurementDataType - if newList != nil { - newData = newList.(*MeasurementListDataType).MeasurementData - } - - r.MeasurementData = UpdateList(r.MeasurementData, newData, filterPartial, filterDelete) -} - -// MeasurementSeriesListDataType - -var _ Updater = (*MeasurementSeriesListDataType)(nil) - -func (r *MeasurementSeriesListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []MeasurementSeriesDataType - if newList != nil { - newData = newList.(*MeasurementSeriesListDataType).MeasurementSeriesData - } - - r.MeasurementSeriesData = UpdateList(r.MeasurementSeriesData, newData, filterPartial, filterDelete) -} - -// MeasurementConstraintsListDataType - -var _ Updater = (*MeasurementConstraintsListDataType)(nil) - -func (r *MeasurementConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []MeasurementConstraintsDataType - if newList != nil { - newData = newList.(*MeasurementConstraintsListDataType).MeasurementConstraintsData - } - - r.MeasurementConstraintsData = UpdateList(r.MeasurementConstraintsData, newData, filterPartial, filterDelete) -} - -// MeasurementDescriptionListDataType - -var _ Updater = (*MeasurementDescriptionListDataType)(nil) - -func (r *MeasurementDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []MeasurementDescriptionDataType - if newList != nil { - newData = newList.(*MeasurementDescriptionListDataType).MeasurementDescriptionData - } - - r.MeasurementDescriptionData = UpdateList(r.MeasurementDescriptionData, newData, filterPartial, filterDelete) -} - -// MeasurementThresholdRelationListDataType - -var _ Updater = (*MeasurementThresholdRelationListDataType)(nil) - -func (r *MeasurementThresholdRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []MeasurementThresholdRelationDataType - if newList != nil { - newData = newList.(*MeasurementThresholdRelationListDataType).MeasurementThresholdRelationData - } - - r.MeasurementThresholdRelationData = UpdateList(r.MeasurementThresholdRelationData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/measurement_additions_test.go b/spine/model/measurement_additions_test.go deleted file mode 100644 index 81a63831..00000000 --- a/spine/model/measurement_additions_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestMeasurementListDataType_Update_Add(t *testing.T) { - sut := MeasurementListDataType{ - MeasurementData: []MeasurementDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(0)), - ValueType: util.Ptr(MeasurementValueTypeTypeAverageValue), - Value: NewScaledNumberType(1), - }, - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ValueType: util.Ptr(MeasurementValueTypeTypeAverageValue), - Value: NewScaledNumberType(1), - }, - }, - } - - newData := MeasurementListDataType{ - MeasurementData: []MeasurementDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ValueType: util.Ptr(MeasurementValueTypeTypeValue), - Value: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.MeasurementData - // check the non changing items - assert.Equal(t, 3, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.MeasurementId)) - assert.Equal(t, MeasurementValueTypeTypeAverageValue, *item1.ValueType) - assert.Equal(t, 1.0, item1.Value.GetValue()) - item2 := data[1] - assert.Equal(t, 1, int(*item2.MeasurementId)) - assert.Equal(t, MeasurementValueTypeTypeAverageValue, *item2.ValueType) - assert.Equal(t, 1.0, item2.Value.GetValue()) -} - -func TestMeasurementListDataType_Update_Replace(t *testing.T) { - sut := MeasurementListDataType{ - MeasurementData: []MeasurementDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(0)), - ValueType: util.Ptr(MeasurementValueTypeTypeAverageValue), - Value: NewScaledNumberType(1), - }, - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ValueType: util.Ptr(MeasurementValueTypeTypeValue), - Value: NewScaledNumberType(1), - }, - }, - } - - newData := MeasurementListDataType{ - MeasurementData: []MeasurementDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ValueType: util.Ptr(MeasurementValueTypeTypeValue), - Value: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.MeasurementData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.MeasurementId)) - assert.Equal(t, MeasurementValueTypeTypeAverageValue, *item1.ValueType) - assert.Equal(t, 1.0, item1.Value.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.MeasurementId)) - assert.Equal(t, MeasurementValueTypeTypeValue, *item2.ValueType) - assert.Equal(t, 10.0, item2.Value.GetValue()) -} - -func TestMeasurementConstraintsListDataType_Update(t *testing.T) { - sut := MeasurementConstraintsListDataType{ - MeasurementConstraintsData: []MeasurementConstraintsDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(0)), - ValueStepSize: NewScaledNumberType(1), - }, - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ValueStepSize: NewScaledNumberType(1), - }, - }, - } - - newData := MeasurementConstraintsListDataType{ - MeasurementConstraintsData: []MeasurementConstraintsDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ValueStepSize: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.MeasurementConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.MeasurementId)) - assert.Equal(t, 1.0, item1.ValueStepSize.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.MeasurementId)) - assert.Equal(t, 10.0, item2.ValueStepSize.GetValue()) -} - -func TestMeasurementDescriptionListDataType_Update(t *testing.T) { - sut := MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []MeasurementDescriptionDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(0)), - ScopeType: util.Ptr(ScopeTypeTypeACCurrent), - }, - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ScopeType: util.Ptr(ScopeTypeTypeACCurrent), - }, - }, - } - - newData := MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []MeasurementDescriptionDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ScopeType: util.Ptr(ScopeTypeTypeACPower), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.MeasurementDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.MeasurementId)) - assert.Equal(t, ScopeTypeTypeACCurrent, *item1.ScopeType) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.MeasurementId)) - assert.Equal(t, ScopeTypeTypeACPower, *item2.ScopeType) -} - -func TestMeasurementThresholdRelationListDataType_Update(t *testing.T) { - sut := MeasurementThresholdRelationListDataType{ - MeasurementThresholdRelationData: []MeasurementThresholdRelationDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(0)), - ThresholdId: []ThresholdIdType{ThresholdIdType(0)}, - }, - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ThresholdId: []ThresholdIdType{ThresholdIdType(0)}, - }, - }, - } - - newData := MeasurementThresholdRelationListDataType{ - MeasurementThresholdRelationData: []MeasurementThresholdRelationDataType{ - { - MeasurementId: util.Ptr(MeasurementIdType(1)), - ThresholdId: []ThresholdIdType{ThresholdIdType(1)}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.MeasurementThresholdRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.MeasurementId)) - assert.Equal(t, 0, int(item1.ThresholdId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.MeasurementId)) - assert.Equal(t, 1, int(item2.ThresholdId[0])) -} diff --git a/spine/model/messaging.go b/spine/model/messaging.go deleted file mode 100644 index 07c606d1..00000000 --- a/spine/model/messaging.go +++ /dev/null @@ -1,39 +0,0 @@ -package model - -type MessagingNumberType uint - -type MessagingDataTextType string - -type MessagingTypeType string - -const ( - MessagingTypeTypeLogging MessagingTypeType = "logging" - MessagingTypeTypeInformation MessagingTypeType = "information" - MessagingTypeTypeWarning MessagingTypeType = "warning" - MessagingTypeTypeAlarm MessagingTypeType = "alarm" - MessagingTypeTypeEmergency MessagingTypeType = "emergency" - MessagingTypeTypeObsolete MessagingTypeType = "obsolete" -) - -type MessagingDataType struct { - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - MessagingNumber *MessagingNumberType `json:"messagingNumber,omitempty" eebus:"key"` - MessagingType *MessagingTypeType `json:"type,omitempty"` // xsd defines "type", but that is a reserved keyword - Text *MessagingDataTextType `json:"text,omitempty"` -} - -type MessagingDataElementsType struct { - Timestamp *ElementTagType `json:"timestamp,omitempty"` - MessagingNumber *ElementTagType `json:"messagingNumber,omitempty"` - MessagingType *ElementTagType `json:"type,omitempty"` - Text *ElementTagType `json:"text,omitempty"` -} - -type MessagingListDataType struct { - MessagingData []MessagingDataType `json:"messagingData,omitempty"` -} - -type MessagingListDataSelectorsType struct { - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` - MessagingNumber *MessagingNumberType `json:"messagingNumber,omitempty"` -} diff --git a/spine/model/messaging_additions.go b/spine/model/messaging_additions.go deleted file mode 100644 index d8b89a0d..00000000 --- a/spine/model/messaging_additions.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -// MessagingListDataType - -var _ Updater = (*MessagingListDataType)(nil) - -func (r *MessagingListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []MessagingDataType - if newList != nil { - newData = newList.(*MessagingListDataType).MessagingData - } - - r.MessagingData = UpdateList(r.MessagingData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/messaging_additions_test.go b/spine/model/messaging_additions_test.go deleted file mode 100644 index 7cf3f884..00000000 --- a/spine/model/messaging_additions_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestMessagingListDataType_Update(t *testing.T) { - sut := MessagingListDataType{ - MessagingData: []MessagingDataType{ - { - MessagingNumber: util.Ptr(MessagingNumberType(0)), - Text: util.Ptr(MessagingDataTextType("old")), - }, - { - MessagingNumber: util.Ptr(MessagingNumberType(1)), - Text: util.Ptr(MessagingDataTextType("old")), - }, - }, - } - - newData := MessagingListDataType{ - MessagingData: []MessagingDataType{ - { - MessagingNumber: util.Ptr(MessagingNumberType(1)), - Text: util.Ptr(MessagingDataTextType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.MessagingData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.MessagingNumber)) - assert.Equal(t, "old", string(*item1.Text)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.MessagingNumber)) - assert.Equal(t, "new", string(*item2.Text)) -} diff --git a/spine/model/networkmanagement.go b/spine/model/networkmanagement.go deleted file mode 100644 index 7c6d37c9..00000000 --- a/spine/model/networkmanagement.go +++ /dev/null @@ -1,239 +0,0 @@ -package model - -type NetworkManagementNativeSetupType string - -type NetworkManagementScanSetupType string - -type NetworkManagementSetupType string - -type NetworkManagementCandidateSetupType string - -type NetworkManagementTechnologyAddressType string - -type NetworkManagementCommunicationsTechnologyInformationType string - -type NetworkManagementMinimumTrustLevelType string - -type NetworkManagementProcessTimeoutType DurationType - -type NetworkManagementFeatureSetType string - -const ( - NetworkManagementFeatureSetTypeGateway NetworkManagementFeatureSetType = "gateway" - NetworkManagementFeatureSetTypeRouter NetworkManagementFeatureSetType = "router" - NetworkManagementFeatureSetTypeSmart NetworkManagementFeatureSetType = "smart" - NetworkManagementFeatureSetTypeSimple NetworkManagementFeatureSetType = "simple" -) - -type NetworkManagementProcessStateStateType string - -const ( - NetworkManagementProcessStateStateTypeSucceeded NetworkManagementProcessStateStateType = "succeeded" - NetworkManagementProcessStateStateTypeFailed NetworkManagementProcessStateStateType = "failed" - NetworkManagementProcessStateStateTypeAborted NetworkManagementProcessStateStateType = "aborted" -) - -type NetworkManagementStateChangeType string - -const ( - NetworkManagementStateChangeTypeAdded NetworkManagementStateChangeType = "added" - NetworkManagementStateChangeTypeRemoved NetworkManagementStateChangeType = "removed" - NetworkManagementStateChangeTypeModified NetworkManagementStateChangeType = "modified" -) - -type NetworkManagementAddNodeCallType struct { - NodeAddress *FeatureAddressType `json:"nodeAddress,omitempty"` - NativeSetup *NetworkManagementNativeSetupType `json:"nativeSetup,omitempty"` - Timeout *NetworkManagementProcessTimeoutType `json:"timeout,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type NetworkManagementAddNodeCallElementsType struct { - NodeAddress *FeatureAddressElementsType `json:"nodeAddress,omitempty"` - NativeSetup *ElementTagType `json:"nativeSetup,omitempty"` - Timeout *ElementTagType `json:"timeout,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type NetworkManagementRemoveNodeCallType struct { - NodeAddress *FeatureAddressType `json:"nodeAddress,omitempty"` - Timeout *NetworkManagementProcessTimeoutType `json:"timeout,omitempty"` -} - -type NetworkManagementRemoveNodeCallElementsType struct { - NodeAddress *FeatureAddressElementsType `json:"nodeAddress,omitempty"` - Timeout *ElementTagType `json:"timeout,omitempty"` -} - -type NetworkManagementModifyNodeCallType struct { - NodeAddress *FeatureAddressType `json:"nodeAddress,omitempty"` - NativeSetup *NetworkManagementNativeSetupType `json:"nativeSetup,omitempty"` - Timeout *NetworkManagementProcessTimeoutType `json:"timeout,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type NetworkManagementModifyNodeCallElementsType struct { - NodeAddress *FeatureAddressElementsType `json:"nodeAddress,omitempty"` - NativeSetup *ElementTagType `json:"nativeSetup,omitempty"` - Timeout *ElementTagType `json:"timeout,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type NetworkManagementScanNetworkCallType struct { - ScanSetup *NetworkManagementScanSetupType `json:"scanSetup,omitempty"` - Timeout *NetworkManagementProcessTimeoutType `json:"timeout,omitempty"` -} - -type NetworkManagementScanNetworkCallElementsType struct { - ScanSetup *ElementTagType `json:"scanSetup,omitempty"` - Timeout *ElementTagType `json:"timeout,omitempty"` -} - -type NetworkManagementDiscoverCallType struct { - DiscoverAddress *FeatureAddressType `json:"discoverAddress,omitempty"` -} - -type NetworkManagementDiscoverCallElementsType struct { - DiscoverAddress *FeatureAddressElementsType `json:"discoverAddress,omitempty"` -} - -type NetworkManagementAbortCallType struct{} - -type NetworkManagementAbortCallElementsType struct{} - -type NetworkManagementProcessStateDataType struct { - State *NetworkManagementProcessStateStateType `json:"state,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type NetworkManagementProcessStateDataElementsType struct { - State *ElementTagType `json:"state,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type NetworkManagementJoiningModeDataType struct { - Setup *NetworkManagementSetupType `json:"setup,omitempty"` -} - -type NetworkManagementJoiningModeDataElementsType struct { - Setup *ElementTagType `json:"setup,omitempty"` -} - -type NetworkManagementReportCandidateDataType struct { - CandidateSetup *NetworkManagementCandidateSetupType `json:"candidateSetup,omitempty"` - SetupUsableForAdd *bool `json:"setupUsableForAdd,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type NetworkManagementReportCandidateDataElementsType struct { - CandidateSetup *ElementTagType `json:"candidateSetup,omitempty"` - SetupUsableForAdd *ElementTagType `json:"setupUsableForAdd,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type NetworkManagementDeviceDescriptionDataType struct { - DeviceAddress *DeviceAddressType `json:"deviceAddress,omitempty"` - DeviceType *DeviceTypeType `json:"deviceType,omitempty"` - NetworkManagementResponsibleAddress *FeatureAddressType `json:"networkManagementResponsibleAddress,omitempty"` - NativeSetup *NetworkManagementNativeSetupType `json:"nativeSetup,omitempty"` - TechnologyAddress *NetworkManagementTechnologyAddressType `json:"technologyAddress,omitempty"` - CommunicationsTechnologyInformation *NetworkManagementCommunicationsTechnologyInformationType `json:"communicationsTechnologyInformation,omitempty"` - NetworkFeatureSet *NetworkManagementFeatureSetType `json:"networkFeatureSet,omitempty"` - LastStateChange *NetworkManagementStateChangeType `json:"lastStateChange,omitempty"` - MinimumTrustLevel *NetworkManagementMinimumTrustLevelType `json:"minimumTrustLevel,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type NetworkManagementDeviceDescriptionDataElementsType struct { - DeviceAddress *ElementTagType `json:"deviceAddress,omitempty"` - DeviceType *ElementTagType `json:"deviceType,omitempty"` - NetworkManagementResponsibleAddress *ElementTagType `json:"networkManagementResponsibleAddress,omitempty"` - NativeSetup *ElementTagType `json:"nativeSetup,omitempty"` - TechnologyAddress *ElementTagType `json:"technologyAddress,omitempty"` - CommunicationsTechnologyInformation *ElementTagType `json:"communicationsTechnologyInformation,omitempty"` - NetworkFeatureSet *ElementTagType `json:"networkFeatureSet,omitempty"` - LastStateChange *ElementTagType `json:"lastStateChange,omitempty"` - MinimumTrustLevel *ElementTagType `json:"minimumTrustLevel,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type NetworkManagementDeviceDescriptionListDataType struct { - NetworkManagementDeviceDescriptionData []NetworkManagementDeviceDescriptionDataType `json:"networkManagementDeviceDescriptionData,omitempty"` -} - -type NetworkManagementDeviceDescriptionListDataSelectorsType struct { - DeviceAddress *DeviceAddressType `json:"deviceAddress,omitempty"` - DeviceType *DeviceTypeType `json:"deviceType,omitempty"` -} - -type NetworkManagementEntityDescriptionDataType struct { - EntityAddress *EntityAddressType `json:"entityAddress,omitempty"` - EntityType *EntityTypeType `json:"entityType,omitempty"` - LastStateChange *NetworkManagementStateChangeType `json:"lastStateChange,omitempty"` - MinimumTrustLevel *NetworkManagementMinimumTrustLevelType `json:"minimumTrustLevel,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type NetworkManagementEntityDescriptionDataElementsType struct { - EntityAddress *ElementTagType `json:"entityAddress,omitempty"` - EntityType *ElementTagType `json:"entityType,omitempty"` - LastStateChange *ElementTagType `json:"lastStateChange,omitempty"` - MinimumTrustLevel *ElementTagType `json:"minimumTrustLevel,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type NetworkManagementEntityDescriptionListDataType struct { - NetworkManagementEntityDescriptionData []NetworkManagementEntityDescriptionDataType `json:"networkManagementEntityDescriptionData,omitempty"` -} - -type NetworkManagementEntityDescriptionListDataSelectorsType struct { - EntityAddress *EntityAddressType `json:"entityAddress,omitempty"` - EntityType *EntityTypeType `json:"entityType,omitempty"` -} - -type NetworkManagementFeatureDescriptionDataType struct { - FeatureAddress *FeatureAddressType `json:"featureAddress,omitempty"` - FeatureType *FeatureTypeType `json:"featureType,omitempty"` - SpecificUsage []FeatureSpecificUsageType `json:"specificUsage,omitempty"` - FeatureGroup *FeatureGroupType `json:"featureGroup,omitempty"` - Role *RoleType `json:"role,omitempty"` - SupportedFunction []FunctionPropertyType `json:"supportedFunction,omitempty"` - LastStateChange *NetworkManagementStateChangeType `json:"lastStateChange,omitempty"` - MinimumTrustLevel *NetworkManagementMinimumTrustLevelType `json:"minimumTrustLevel,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` - MaxResponseDelay *MaxResponseDelayType `json:"maxResponseDelay,omitempty"` -} - -type NetworkManagementFeatureDescriptionDataElementsType struct { - FeatureAddress *FeatureAddressElementsType `json:"featureAddress,omitempty"` - FeatureType *ElementTagType `json:"featureType,omitempty"` - SpecificUsage *ElementTagType `json:"specificUsage,omitempty"` - FeatureGroup *ElementTagType `json:"featureGroup,omitempty"` - Role *ElementTagType `json:"role,omitempty"` - SupportedFunction *FunctionPropertyElementsType `json:"supportedFunction,omitempty"` - LastStateChange *ElementTagType `json:"lastStateChange,omitempty"` - MinimumTrustLevel *ElementTagType `json:"minimumTrustLevel,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` - MaxResponseDelay *ElementTagType `json:"maxResponseDelay,omitempty"` -} - -type NetworkManagementFeatureDescriptionListDataType struct { - NetworkManagementFeatureDescriptionData []NetworkManagementFeatureDescriptionDataType `json:"networkManagementFeatureDescriptionData,omitempty"` -} - -type NetworkManagementFeatureDescriptionListDataSelectorsType struct { - FeatureAddress *FeatureAddressType `json:"featureAddress,omitempty"` - FeatureType *FeatureTypeType `json:"featureType,omitempty"` -} diff --git a/spine/model/networkmanagement_additions.go b/spine/model/networkmanagement_additions.go deleted file mode 100644 index fbca8e81..00000000 --- a/spine/model/networkmanagement_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// NetworkManagementDeviceDescriptionListDataType - -var _ Updater = (*NetworkManagementDeviceDescriptionListDataType)(nil) - -func (r *NetworkManagementDeviceDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []NetworkManagementDeviceDescriptionDataType - if newList != nil { - newData = newList.(*NetworkManagementDeviceDescriptionListDataType).NetworkManagementDeviceDescriptionData - } - - r.NetworkManagementDeviceDescriptionData = UpdateList(r.NetworkManagementDeviceDescriptionData, newData, filterPartial, filterDelete) -} - -// NetworkManagementEntityDescriptionListDataType - -var _ Updater = (*NetworkManagementEntityDescriptionListDataType)(nil) - -func (r *NetworkManagementEntityDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []NetworkManagementEntityDescriptionDataType - if newList != nil { - newData = newList.(*NetworkManagementEntityDescriptionListDataType).NetworkManagementEntityDescriptionData - } - - r.NetworkManagementEntityDescriptionData = UpdateList(r.NetworkManagementEntityDescriptionData, newData, filterPartial, filterDelete) -} - -// NetworkManagementFeatureDescriptionListDataType - -var _ Updater = (*NetworkManagementFeatureDescriptionListDataType)(nil) - -func (r *NetworkManagementFeatureDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []NetworkManagementFeatureDescriptionDataType - if newList != nil { - newData = newList.(*NetworkManagementFeatureDescriptionListDataType).NetworkManagementFeatureDescriptionData - } - - r.NetworkManagementFeatureDescriptionData = UpdateList(r.NetworkManagementFeatureDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/nodemanagement.go b/spine/model/nodemanagement.go deleted file mode 100644 index afbd6e4a..00000000 --- a/spine/model/nodemanagement.go +++ /dev/null @@ -1,136 +0,0 @@ -package model - -type NodeManagementSpecificationVersionListType struct { - SpecificationVersion []SpecificationVersionDataType `json:"specificationVersion,omitempty"` -} -type NodeManagementSpecificationVersionListElementsType struct { - SpecificationVersion *SpecificationVersionDataElementsType `json:"specificationVersion,omitempty"` -} - -type NodeManagementDetailedDiscoveryDeviceInformationType struct { - Description *NetworkManagementDeviceDescriptionDataType `json:"description,omitempty"` -} - -type NodeManagementDetailedDiscoveryDeviceInformationElementsType struct { - Description *NetworkManagementDeviceDescriptionDataElementsType `json:"description,omitempty"` -} - -type NodeManagementDetailedDiscoveryEntityInformationType struct { - Description *NetworkManagementEntityDescriptionDataType `json:"description,omitempty"` -} - -type NodeManagementDetailedDiscoveryEntityInformationElementsType struct { - Description *NetworkManagementEntityDescriptionDataElementsType `json:"description,omitempty"` -} - -type NodeManagementDetailedDiscoveryFeatureInformationType struct { - Description *NetworkManagementFeatureDescriptionDataType `json:"description,omitempty"` -} - -type NodeManagementDetailedDiscoveryFeatureInformationElementsType struct { - Description *NetworkManagementFeatureDescriptionDataElementsType `json:"description,omitempty"` -} - -type NodeManagementDetailedDiscoveryDataType struct { - SpecificationVersionList *NodeManagementSpecificationVersionListType `json:"specificationVersionList,omitempty"` - DeviceInformation *NodeManagementDetailedDiscoveryDeviceInformationType `json:"deviceInformation,omitempty"` - EntityInformation []NodeManagementDetailedDiscoveryEntityInformationType `json:"entityInformation,omitempty"` - FeatureInformation []NodeManagementDetailedDiscoveryFeatureInformationType `json:"featureInformation,omitempty"` -} - -type NodeManagementDetailedDiscoveryDataElementsType struct { - SpecificationVersionList *NodeManagementSpecificationVersionListElementsType `json:"specificationVersionList,omitempty"` - DeviceInformation *NodeManagementDetailedDiscoveryDeviceInformationElementsType `json:"deviceInformation,omitempty"` - EntityInformation *NodeManagementDetailedDiscoveryEntityInformationElementsType `json:"entityInformation,omitempty"` - FeatureInformation *NodeManagementDetailedDiscoveryFeatureInformationElementsType `json:"featureInformation,omitempty"` -} - -type NodeManagementDetailedDiscoveryDataSelectorsType struct { - DeviceInformation *NetworkManagementDeviceDescriptionListDataSelectorsType `json:"deviceInformation,omitempty"` - EntityInformation *NetworkManagementEntityDescriptionListDataSelectorsType `json:"entityInformation,omitempty"` - FeatureInformation *NetworkManagementFeatureDescriptionListDataSelectorsType `json:"featureInformation,omitempty"` -} - -type NodeManagementBindingDataType struct { - BindingEntry []BindingManagementEntryDataType `json:"bindingEntry,omitempty"` -} - -type NodeManagementBindingDataElementsType struct { - BindingEntry *BindingManagementEntryDataElementsType `json:"bindingEntry,omitempty"` -} - -type NodeManagementBindingDataSelectorsType struct { - BindingEntry *BindingManagementEntryListDataSelectorsType `json:"bindingEntry,omitempty"` -} - -type NodeManagementBindingRequestCallType struct { - BindingRequest *BindingManagementRequestCallType `json:"bindingRequest,omitempty"` -} - -type NodeManagementBindingRequestCallElementsType struct { - BindingRequest *BindingManagementRequestCallElementsType `json:"bindingRequest,omitempty"` -} - -type NodeManagementBindingDeleteCallType struct { - BindingDelete *BindingManagementDeleteCallType `json:"bindingDelete,omitempty"` -} - -type NodeManagementBindingDeleteCallElementsType struct { - BindingDelete *BindingManagementDeleteCallElementsType `json:"bindingDelete,omitempty"` -} - -type NodeManagementSubscriptionDataType struct { - SubscriptionEntry []SubscriptionManagementEntryDataType `json:"subscriptionEntry,omitempty"` -} - -type NodeManagementSubscriptionDataElementsType struct { - SubscriptionEntry *SubscriptionManagementEntryDataElementsType `json:"subscriptionEntry,omitempty"` -} - -type NodeManagementSubscriptionDataSelectorsType struct { - SubscriptionEntry *SubscriptionManagementEntryListDataSelectorsType `json:"subscriptionEntry,omitempty"` -} - -type NodeManagementSubscriptionRequestCallType struct { - SubscriptionRequest *SubscriptionManagementRequestCallType `json:"subscriptionRequest,omitempty"` -} - -type NodeManagementSubscriptionRequestCallElementsType struct { - SubscriptionRequest *SubscriptionManagementRequestCallElementsType `json:"subscriptionRequest,omitempty"` -} - -type NodeManagementSubscriptionDeleteCallType struct { - SubscriptionDelete *SubscriptionManagementDeleteCallType `json:"subscriptionDelete,omitempty"` -} - -type NodeManagementSubscriptionDeleteCallElementsType struct { - SubscriptionDelete *SubscriptionManagementDeleteCallElementsType `json:"subscriptionDelete,omitempty"` -} - -type NodeManagementDestinationDataType struct { - DeviceDescription *NetworkManagementDeviceDescriptionDataType `json:"deviceDescription,omitempty"` -} - -type NodeManagementDestinationDataElementsType struct { - DeviceDescription *NetworkManagementDeviceDescriptionDataElementsType `json:"deviceDescription,omitempty"` -} - -type NodeManagementDestinationListDataType struct { - NodeManagementDestinationData []NodeManagementDestinationDataType `json:"nodeManagementDestinationData,omitempty"` -} - -type NodeManagementDestinationListDataSelectorsType struct { - DeviceDescription *NetworkManagementDeviceDescriptionListDataSelectorsType `json:"deviceDescription,omitempty"` -} - -type NodeManagementUseCaseDataType struct { - UseCaseInformation []UseCaseInformationDataType `json:"useCaseInformation,omitempty"` -} - -type NodeManagementUseCaseDataElementsType struct { - UseCaseInformation *UseCaseInformationDataElementsType `json:"useCaseInformation,omitempty"` -} - -type NodeManagementUseCaseDataSelectorsType struct { - UseCaseInformation *UseCaseInformationListDataSelectorsType `json:"useCaseInformation,omitempty"` -} diff --git a/spine/model/nodemanagement_additions.go b/spine/model/nodemanagement_additions.go deleted file mode 100644 index 1ad61bca..00000000 --- a/spine/model/nodemanagement_additions.go +++ /dev/null @@ -1,143 +0,0 @@ -package model - -import ( - "reflect" - "sync" -) - -var nmMux sync.Mutex - -// NodeManagementDestinationListDataType - -var _ Updater = (*NodeManagementDestinationListDataType)(nil) - -func (r *NodeManagementDestinationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []NodeManagementDestinationDataType - if newList != nil { - newData = newList.(*NodeManagementDestinationListDataType).NodeManagementDestinationData - } - - r.NodeManagementDestinationData = UpdateList(r.NodeManagementDestinationData, newData, filterPartial, filterDelete) -} - -// NodeManagementUseCaseDataType - -// find the matching UseCaseInformation index for -// a given FeatureAddressType, UseCaseActorType and UseCaseNameType -// -// if UseCaseActorType and UseCaseNameType are empty they are ignored, -// and the first matching UseCaseInformation item is returned -func (n *NodeManagementUseCaseDataType) useCaseInformationIndex( - address FeatureAddressType, - actor UseCaseActorType, - useCaseName UseCaseNameType, -) (int, bool) { - - // get the element with the same entity - for index, item := range n.UseCaseInformation { - if item.Address.Device == nil || - item.Address.Entity == nil || - !reflect.DeepEqual(item.Address.Device, address.Device) || - !reflect.DeepEqual(item.Address.Entity, address.Entity) { - continue - } - - if len(actor) == 0 && len(useCaseName) == 0 { - return index, true - } - - if len(actor) > 0 { - if item.Actor == nil || *item.Actor != actor { - continue - } - - } - - if len(useCaseName) == 0 { - return index, true - } - - if _, ok := item.useCaseSupportIndex(useCaseName); ok { - return index, true - } - } - - return -1, false -} - -// add a new UseCaseSupportType -func (n *NodeManagementUseCaseDataType) AddUseCaseSupport( - address FeatureAddressType, - actor UseCaseActorType, - useCaseName UseCaseNameType, - useCaseVersion SpecificationVersionType, - useCaseDocumemtSubRevision string, - useCaseAvailable bool, - scenarios []UseCaseScenarioSupportType, -) { - nmMux.Lock() - defer nmMux.Unlock() - - useCaseSupport := UseCaseSupportType{ - UseCaseName: &useCaseName, - UseCaseVersion: &useCaseVersion, - UseCaseAvailable: &useCaseAvailable, - ScenarioSupport: scenarios, - UseCaseDocumentSubRevision: &useCaseDocumemtSubRevision, - } - - // is there an entry for the entity address and actor - usecaseIndex, ok := n.useCaseInformationIndex(address, actor, "") - - if ok { - n.UseCaseInformation[usecaseIndex].Add(useCaseSupport) - } else { - // create a new element for this entity - useCaseInformation := UseCaseInformationDataType{ - Address: &FeatureAddressType{ - Device: address.Device, - Entity: address.Entity, - }, - Actor: &actor, - UseCaseSupport: []UseCaseSupportType{useCaseSupport}, - } - n.UseCaseInformation = append(n.UseCaseInformation, useCaseInformation) - } -} - -// Remove a UseCaseSupportType with -// a provided FeatureAddressType, UseCaseActorType and UseCaseNameType -func (n *NodeManagementUseCaseDataType) RemoveUseCaseSupport( - address FeatureAddressType, - actor UseCaseActorType, - useCaseName UseCaseNameType, -) { - nmMux.Lock() - defer nmMux.Unlock() - - // is there an entry for the entity address, actor and usecase name - usecaseIndex, ok := n.useCaseInformationIndex(address, actor, useCaseName) - if !ok { - return - } - - var usecaseInfo []UseCaseInformationDataType - - for index, item := range n.UseCaseInformation { - if index != usecaseIndex { - usecaseInfo = append(usecaseInfo, item) - continue - } - - item.Remove(useCaseName) - - // only add the item if there are any usecases left - if len(item.UseCaseSupport) == 0 { - continue - } - - usecaseInfo = append(usecaseInfo, item) - } - - n.UseCaseInformation = usecaseInfo -} diff --git a/spine/model/nodemanagement_additions_test.go b/spine/model/nodemanagement_additions_test.go deleted file mode 100644 index 8a402eec..00000000 --- a/spine/model/nodemanagement_additions_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestNodeManagementUseCaseDataTypeSuite(t *testing.T) { - suite.Run(t, new(NodeManagementUseCaseDataTypeSuite)) -} - -type NodeManagementUseCaseDataTypeSuite struct { - suite.Suite -} - -func (s *NodeManagementUseCaseDataTypeSuite) SetupSuite() {} -func (s *NodeManagementUseCaseDataTypeSuite) TearDownTest() {} - -func (s *NodeManagementUseCaseDataTypeSuite) BeforeTest(suiteName, testName string) {} - -func (s *NodeManagementUseCaseDataTypeSuite) Test_AdditionsAndRemovals() { - ucs := &NodeManagementUseCaseDataType{} - assert.NotNil(s.T(), ucs) - assert.Equal(s.T(), 0, len(ucs.UseCaseInformation)) - - address := FeatureAddressType{ - Device: util.Ptr(AddressDeviceType("test")), - Entity: []AddressEntityType{1}, - } - ucs.AddUseCaseSupport( - address, - UseCaseActorTypeCEM, - UseCaseNameTypeControlOfBattery, - SpecificationVersionType(""), - "", - true, - []UseCaseScenarioSupportType{}, - ) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation[0].UseCaseSupport)) - - ucs.AddUseCaseSupport( - address, - UseCaseActorTypeCEM, - UseCaseNameTypeEVSECommissioningAndConfiguration, - SpecificationVersionType(""), - "", - true, - []UseCaseScenarioSupportType{}, - ) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) - assert.Equal(s.T(), 2, len(ucs.UseCaseInformation[0].UseCaseSupport)) - - ucs.AddUseCaseSupport( - address, - UseCaseActorTypeCEM, - UseCaseNameTypeEVSECommissioningAndConfiguration, - SpecificationVersionType(""), - "", - true, - []UseCaseScenarioSupportType{}, - ) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) - assert.Equal(s.T(), 2, len(ucs.UseCaseInformation[0].UseCaseSupport)) - - ucs.AddUseCaseSupport( - address, - UseCaseActorTypeEnergyGuard, - UseCaseNameTypeLimitationOfPowerConsumption, - SpecificationVersionType(""), - "", - true, - []UseCaseScenarioSupportType{}, - ) - assert.Equal(s.T(), 2, len(ucs.UseCaseInformation)) - assert.Equal(s.T(), 2, len(ucs.UseCaseInformation[0].UseCaseSupport)) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation[1].UseCaseSupport)) - - ucs.RemoveUseCaseSupport( - address, - UseCaseActorTypeCEM, - UseCaseNameTypeEVChargingSummary, - ) - assert.Equal(s.T(), 2, len(ucs.UseCaseInformation)) - assert.Equal(s.T(), 2, len(ucs.UseCaseInformation[0].UseCaseSupport)) - - ucs.RemoveUseCaseSupport( - address, - UseCaseActorTypeCEM, - UseCaseNameTypeControlOfBattery, - ) - assert.Equal(s.T(), 2, len(ucs.UseCaseInformation)) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation[0].UseCaseSupport)) - - ucs.RemoveUseCaseSupport( - address, - UseCaseActorTypeCEM, - UseCaseNameTypeEVSECommissioningAndConfiguration, - ) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) - - ucs.RemoveUseCaseSupport( - address, - "", - "", - ) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) - - invalidAddress := FeatureAddressType{ - Device: util.Ptr(AddressDeviceType("test")), - Entity: []AddressEntityType{2}, - } - ucs.RemoveUseCaseSupport( - invalidAddress, - UseCaseActorTypeCEM, - UseCaseNameTypeEVSECommissioningAndConfiguration, - ) - assert.Equal(s.T(), 1, len(ucs.UseCaseInformation)) - -} diff --git a/spine/model/operatingconstraints.go b/spine/model/operatingconstraints.go deleted file mode 100644 index 5d760489..00000000 --- a/spine/model/operatingconstraints.go +++ /dev/null @@ -1,143 +0,0 @@ -package model - -type OperatingConstraintsInterruptDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - IsPausable *bool `json:"isPausable,omitempty"` - IsStoppable *bool `json:"isStoppable,omitempty"` - NotInterruptibleAtHighPower *bool `json:"notInterruptibleAtHighPower,omitempty"` - MaxCyclesPerDay *uint `json:"maxCyclesPerDay,omitempty"` -} - -type OperatingConstraintsInterruptDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - IsPausable *ElementTagType `json:"isPausable,omitempty"` - IsStoppable *ElementTagType `json:"isStoppable,omitempty"` - NotInterruptibleAtHighPower *ElementTagType `json:"notInterruptibleAtHighPower,omitempty"` - MaxCyclesPerDay *ElementTagType `json:"maxCyclesPerDay,omitempty"` -} - -type OperatingConstraintsInterruptListDataType struct { - OperatingConstraintsInterruptData []OperatingConstraintsInterruptDataType `json:"operatingConstraintsInterruptData,omitempty"` -} - -type OperatingConstraintsInterruptListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type OperatingConstraintsDurationDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - ActiveDurationMin *DurationType `json:"activeDurationMin,omitempty"` - ActiveDurationMax *DurationType `json:"activeDurationMax,omitempty"` - PauseDurationMin *DurationType `json:"pauseDurationMin,omitempty"` - PauseDurationMax *DurationType `json:"pauseDurationMax,omitempty"` - ActiveDurationSumMin *DurationType `json:"activeDurationSumMin,omitempty"` - ActiveDurationSumMax *DurationType `json:"activeDurationSumMax,omitempty"` -} - -type OperatingConstraintsDurationDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - ActiveDurationMin *ElementTagType `json:"activeDurationMin,omitempty"` - ActiveDurationMax *ElementTagType `json:"activeDurationMax,omitempty"` - PauseDurationMin *ElementTagType `json:"pauseDurationMin,omitempty"` - PauseDurationMax *ElementTagType `json:"pauseDurationMax,omitempty"` - ActiveDurationSumMin *ElementTagType `json:"activeDurationSumMin,omitempty"` - ActiveDurationSumMax *ElementTagType `json:"activeDurationSumMax,omitempty"` -} - -type OperatingConstraintsDurationListDataType struct { - OperatingConstraintsDurationData []OperatingConstraintsDurationDataType `json:"operatingConstraintsDurationData,omitempty"` -} - -type OperatingConstraintsDurationListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type OperatingConstraintsPowerDescriptionDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - PositiveEnergyDirection *EnergyDirectionType `json:"positiveEnergyDirection,omitempty"` - PowerUnit *UnitOfMeasurementType `json:"powerUnit,omitempty"` - EnergyUnit *UnitOfMeasurementType `json:"energyUnit,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type OperatingConstraintsPowerDescriptionDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - PositiveEnergyDirection *ElementTagType `json:"positiveEnergyDirection,omitempty"` - PowerUnit *ElementTagType `json:"powerUnit,omitempty"` - EnergyUnit *ElementTagType `json:"energyUnit,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type OperatingConstraintsPowerDescriptionListDataType struct { - OperatingConstraintsPowerDescriptionData []OperatingConstraintsPowerDescriptionDataType `json:"operatingConstraintsPowerDescriptionData,omitempty"` -} - -type OperatingConstraintsPowerDescriptionListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type OperatingConstraintsPowerRangeDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - PowerMin *ScaledNumberType `json:"powerMin,omitempty"` - PowerMax *ScaledNumberType `json:"powerMax,omitempty"` - EnergyMin *ScaledNumberType `json:"energyMin,omitempty"` - EnergyMax *ScaledNumberType `json:"energyMax,omitempty"` -} - -type OperatingConstraintsPowerRangeDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - PowerMin *ElementTagType `json:"powerMin,omitempty"` - PowerMax *ElementTagType `json:"powerMax,omitempty"` - EnergyMin *ElementTagType `json:"energyMin,omitempty"` - EnergyMax *ElementTagType `json:"energyMax,omitempty"` -} - -type OperatingConstraintsPowerRangeListDataType struct { - OperatingConstraintsPowerRangeData []OperatingConstraintsPowerRangeDataType `json:"operatingConstraintsPowerRangeData,omitempty"` -} - -type OperatingConstraintsPowerRangeListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type OperatingConstraintsPowerLevelDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - Power *ScaledNumberType `json:"power,omitempty"` -} - -type OperatingConstraintsPowerLevelDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - Power *ElementTagType `json:"power,omitempty"` -} - -type OperatingConstraintsPowerLevelListDataType struct { - OperatingConstraintsPowerLevelData []OperatingConstraintsPowerLevelDataType `json:"operatingConstraintsPowerLevelData,omitempty"` -} - -type OperatingConstraintsPowerLevelListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type OperatingConstraintsResumeImplicationDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - ResumeEnergyEstimated *ScaledNumberType `json:"resumeEnergyEstimated,omitempty"` - EnergyUnit *UnitOfMeasurementType `json:"energyUnit,omitempty"` - ResumeCostEstimated *ScaledNumberType `json:"resumeCostEstimated,omitempty"` - Currency *CurrencyType `json:"currency,omitempty"` -} - -type OperatingConstraintsResumeImplicationDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - ResumeEnergyEstimated *ScaledNumberElementsType `json:"resumeEnergyEstimated,omitempty"` - EnergyUnit *ElementTagType `json:"energyUnit,omitempty"` - ResumeCostEstimated *ScaledNumberElementsType `json:"resumeCostEstimated,omitempty"` - Currency *ElementTagType `json:"currency,omitempty"` -} - -type OperatingConstraintsResumeImplicationListDataType struct { - OperatingConstraintsResumeImplicationData []OperatingConstraintsResumeImplicationDataType `json:"operatingConstraintsResumeImplicationData,omitempty"` -} - -type OperatingConstraintsResumeImplicationListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} diff --git a/spine/model/operatingconstraints_additions.go b/spine/model/operatingconstraints_additions.go deleted file mode 100644 index 7070c1b3..00000000 --- a/spine/model/operatingconstraints_additions.go +++ /dev/null @@ -1,79 +0,0 @@ -package model - -// OperatingConstraintsInterruptListDataType - -var _ Updater = (*OperatingConstraintsInterruptListDataType)(nil) - -func (r *OperatingConstraintsInterruptListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []OperatingConstraintsInterruptDataType - if newList != nil { - newData = newList.(*OperatingConstraintsInterruptListDataType).OperatingConstraintsInterruptData - } - - r.OperatingConstraintsInterruptData = UpdateList(r.OperatingConstraintsInterruptData, newData, filterPartial, filterDelete) -} - -// OperatingConstraintsDurationListDataType - -var _ Updater = (*OperatingConstraintsDurationListDataType)(nil) - -func (r *OperatingConstraintsDurationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []OperatingConstraintsDurationDataType - if newList != nil { - newData = newList.(*OperatingConstraintsDurationListDataType).OperatingConstraintsDurationData - } - - r.OperatingConstraintsDurationData = UpdateList(r.OperatingConstraintsDurationData, newData, filterPartial, filterDelete) -} - -// OperatingConstraintsPowerDescriptionListDataType - -var _ Updater = (*OperatingConstraintsPowerDescriptionListDataType)(nil) - -func (r *OperatingConstraintsPowerDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []OperatingConstraintsPowerDescriptionDataType - if newList != nil { - newData = newList.(*OperatingConstraintsPowerDescriptionListDataType).OperatingConstraintsPowerDescriptionData - } - - r.OperatingConstraintsPowerDescriptionData = UpdateList(r.OperatingConstraintsPowerDescriptionData, newData, filterPartial, filterDelete) -} - -// OperatingConstraintsPowerRangeListDataType - -var _ Updater = (*OperatingConstraintsPowerRangeListDataType)(nil) - -func (r *OperatingConstraintsPowerRangeListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []OperatingConstraintsPowerRangeDataType - if newList != nil { - newData = newList.(*OperatingConstraintsPowerRangeListDataType).OperatingConstraintsPowerRangeData - } - - r.OperatingConstraintsPowerRangeData = UpdateList(r.OperatingConstraintsPowerRangeData, newData, filterPartial, filterDelete) -} - -// OperatingConstraintsPowerLevelListDataType - -var _ Updater = (*OperatingConstraintsPowerLevelListDataType)(nil) - -func (r *OperatingConstraintsPowerLevelListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []OperatingConstraintsPowerLevelDataType - if newList != nil { - newData = newList.(*OperatingConstraintsPowerLevelListDataType).OperatingConstraintsPowerLevelData - } - - r.OperatingConstraintsPowerLevelData = UpdateList(r.OperatingConstraintsPowerLevelData, newData, filterPartial, filterDelete) -} - -// OperatingConstraintsResumeImplicationListDataType - -var _ Updater = (*OperatingConstraintsResumeImplicationListDataType)(nil) - -func (r *OperatingConstraintsResumeImplicationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []OperatingConstraintsResumeImplicationDataType - if newList != nil { - newData = newList.(*OperatingConstraintsResumeImplicationListDataType).OperatingConstraintsResumeImplicationData - } - - r.OperatingConstraintsResumeImplicationData = UpdateList(r.OperatingConstraintsResumeImplicationData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/operatingconstraints_additions_test.go b/spine/model/operatingconstraints_additions_test.go deleted file mode 100644 index 413b0dba..00000000 --- a/spine/model/operatingconstraints_additions_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package model - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestOperatingConstraintsInterruptListDataType_Update(t *testing.T) { - sut := OperatingConstraintsInterruptListDataType{ - OperatingConstraintsInterruptData: []OperatingConstraintsInterruptDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - IsPausable: util.Ptr(false), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - IsPausable: util.Ptr(false), - }, - }, - } - - newData := OperatingConstraintsInterruptListDataType{ - OperatingConstraintsInterruptData: []OperatingConstraintsInterruptDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - IsPausable: util.Ptr(true), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.OperatingConstraintsInterruptData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, false, *item1.IsPausable) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, true, *item2.IsPausable) -} - -func TestOperatingConstraintsDurationListDataType_Update(t *testing.T) { - sut := OperatingConstraintsDurationListDataType{ - OperatingConstraintsDurationData: []OperatingConstraintsDurationDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - ActiveDurationMin: NewDurationType(1 * time.Second), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - ActiveDurationMin: NewDurationType(1 * time.Second), - }, - }, - } - - newData := OperatingConstraintsDurationListDataType{ - OperatingConstraintsDurationData: []OperatingConstraintsDurationDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - ActiveDurationMin: NewDurationType(10 * time.Second), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.OperatingConstraintsDurationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - duration, _ := item1.ActiveDurationMin.GetTimeDuration() - assert.Equal(t, time.Duration(1*time.Second), duration) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - duration, _ = item2.ActiveDurationMin.GetTimeDuration() - assert.Equal(t, time.Duration(10*time.Second), duration) -} - -func TestOperatingConstraintsPowerDescriptionListDataType_Update(t *testing.T) { - sut := OperatingConstraintsPowerDescriptionListDataType{ - OperatingConstraintsPowerDescriptionData: []OperatingConstraintsPowerDescriptionDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeConsume), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeConsume), - }, - }, - } - - newData := OperatingConstraintsPowerDescriptionListDataType{ - OperatingConstraintsPowerDescriptionData: []OperatingConstraintsPowerDescriptionDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeProduce), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.OperatingConstraintsPowerDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, EnergyDirectionTypeConsume, *item1.PositiveEnergyDirection) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, EnergyDirectionTypeProduce, *item2.PositiveEnergyDirection) -} - -func TestOperatingConstraintsPowerRangeListDataType_Update(t *testing.T) { - sut := OperatingConstraintsPowerRangeListDataType{ - OperatingConstraintsPowerRangeData: []OperatingConstraintsPowerRangeDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - PowerMin: NewScaledNumberType(1), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - PowerMin: NewScaledNumberType(1), - }, - }, - } - - newData := OperatingConstraintsPowerRangeListDataType{ - OperatingConstraintsPowerRangeData: []OperatingConstraintsPowerRangeDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - PowerMin: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.OperatingConstraintsPowerRangeData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, 1.0, item1.PowerMin.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, 10.0, item2.PowerMin.GetValue()) -} - -func TestOperatingConstraintsPowerLevelListDataType_Update(t *testing.T) { - sut := OperatingConstraintsPowerLevelListDataType{ - OperatingConstraintsPowerLevelData: []OperatingConstraintsPowerLevelDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - Power: NewScaledNumberType(1), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - Power: NewScaledNumberType(1), - }, - }, - } - - newData := OperatingConstraintsPowerLevelListDataType{ - OperatingConstraintsPowerLevelData: []OperatingConstraintsPowerLevelDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - Power: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.OperatingConstraintsPowerLevelData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, 1.0, item1.Power.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, 10.0, item2.Power.GetValue()) -} - -func TestOperatingConstraintsResumeImplicationListDataType_Update(t *testing.T) { - sut := OperatingConstraintsResumeImplicationListDataType{ - OperatingConstraintsResumeImplicationData: []OperatingConstraintsResumeImplicationDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - ResumeEnergyEstimated: NewScaledNumberType(1), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - ResumeEnergyEstimated: NewScaledNumberType(1), - }, - }, - } - - newData := OperatingConstraintsResumeImplicationListDataType{ - OperatingConstraintsResumeImplicationData: []OperatingConstraintsResumeImplicationDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - ResumeEnergyEstimated: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.OperatingConstraintsResumeImplicationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, 1.0, item1.ResumeEnergyEstimated.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, 10.0, item2.ResumeEnergyEstimated.GetValue()) -} diff --git a/spine/model/powersequences.go b/spine/model/powersequences.go deleted file mode 100644 index 8ac515ef..00000000 --- a/spine/model/powersequences.go +++ /dev/null @@ -1,331 +0,0 @@ -package model - -type AlternativesIdType uint - -type PowerSequenceIdType uint - -type PowerTimeSlotNumberType uint - -type PowerTimeSlotValueTypeType string - -const ( - PowerTimeSlotValueTypeTypePower PowerTimeSlotValueTypeType = "power" - PowerTimeSlotValueTypeTypePowerMin PowerTimeSlotValueTypeType = "powerMin" - PowerTimeSlotValueTypeTypePowerMax PowerTimeSlotValueTypeType = "powerMax" - PowerTimeSlotValueTypeTypePowerExpectedValue PowerTimeSlotValueTypeType = "powerExpectedValue" - PowerTimeSlotValueTypeTypePowerStandardDeviation PowerTimeSlotValueTypeType = "powerStandardDeviation" - PowerTimeSlotValueTypeTypePowerSkewness PowerTimeSlotValueTypeType = "powerSkewness" - PowerTimeSlotValueTypeTypeEnergy PowerTimeSlotValueTypeType = "energy" - PowerTimeSlotValueTypeTypeEnergyMin PowerTimeSlotValueTypeType = "energyMin" - PowerTimeSlotValueTypeTypeEnergyMax PowerTimeSlotValueTypeType = "energyMax" - PowerTimeSlotValueTypeTypeEnergyExpectedValue PowerTimeSlotValueTypeType = "energyExpectedValue" - PowerTimeSlotValueTypeTypeEnergyStandardDeviation PowerTimeSlotValueTypeType = "energyStandardDeviation" - PowerTimeSlotValueTypeTypeEnergySkewness PowerTimeSlotValueTypeType = "energySkewness" -) - -type PowerSequenceScopeType string - -const ( - PowerSequenceScopeTypeForecast PowerSequenceScopeType = "forecast" - PowerSequenceScopeTypeMeasurement PowerSequenceScopeType = "measurement" - PowerSequenceScopeTypeRecommendation PowerSequenceScopeType = "recommendation" -) - -type PowerSequenceStateType string - -const ( - PowerSequenceStateTypeRunning PowerSequenceStateType = "running" - PowerSequenceStateTypePaused PowerSequenceStateType = "paused" - PowerSequenceStateTypeScheduled PowerSequenceStateType = "scheduled" - PowerSequenceStateTypeScheduledPaused PowerSequenceStateType = "scheduledPaused" - PowerSequenceStateTypePending PowerSequenceStateType = "pending" - PowerSequenceStateTypeInactive PowerSequenceStateType = "inactive" - PowerSequenceStateTypeCompleted PowerSequenceStateType = "completed" - PowerSequenceStateTypeInvalid PowerSequenceStateType = "invalid" -) - -type PowerTimeSlotScheduleDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - SlotNumber *PowerTimeSlotNumberType `json:"slotNumber,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` - DefaultDuration *DurationType `json:"defaultDuration,omitempty"` - DurationUncertainty *DurationType `json:"durationUncertainty,omitempty"` - SlotActivated *bool `json:"slotActivated,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type PowerTimeSlotScheduleDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - SlotNumber *ElementTagType `json:"slotNumber,omitempty"` - TimePeriod *ElementTagType `json:"timePeriod,omitempty"` - DefaultDuration *ElementTagType `json:"defaultDuration,omitempty"` - DurationUncertainty *ElementTagType `json:"durationUncertainty,omitempty"` - SlotActivated *ElementTagType `json:"slotActivated,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type PowerTimeSlotScheduleListDataType struct { - PowerTimeSlotScheduleData []PowerTimeSlotScheduleDataType `json:"powerTimeSlotScheduleData,omitempty"` -} - -type PowerTimeSlotScheduleListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` - SlotNumber *PowerTimeSlotNumberType `json:"slotNumber,omitempty"` -} - -type PowerTimeSlotValueDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - SlotNumber *PowerTimeSlotNumberType `json:"slotNumber,omitempty"` - ValueType *PowerTimeSlotValueTypeType `json:"valueType,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` -} - -type PowerTimeSlotValueDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - SlotNumber *ElementTagType `json:"slotNumber,omitempty"` - ValueType *ElementTagType `json:"valueType,omitempty"` - Value *ScaledNumberElementsType `json:"value,omitempty"` -} - -type PowerTimeSlotValueListDataType struct { - PowerTimeSlotValueData []PowerTimeSlotValueDataType `json:"powerTimeSlotValueListData,omitempty"` -} - -type PowerTimeSlotValueListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` - SlotNumber *PowerTimeSlotNumberType `json:"slotNumber,omitempty"` - ValueType *PowerTimeSlotValueTypeType `json:"valueType,omitempty"` -} - -type PowerTimeSlotScheduleConstraintsDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - SlotNumber *PowerTimeSlotNumberType `json:"slotNumber,omitempty"` - EarliestStartTime *AbsoluteOrRelativeTimeType `json:"earliestStartTime,omitempty"` - LatestEndTime *AbsoluteOrRelativeTimeType `json:"latestEndTime,omitempty"` - MinDuration *DurationType `json:"minDuration,omitempty"` - MaxDuration *DurationType `json:"maxDuration,omitempty"` - OptionalSlot *bool `json:"optionalSlot,omitempty"` -} - -type PowerTimeSlotScheduleConstraintsDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - SlotNumber *ElementTagType `json:"slotNumber,omitempty"` - EarliestStartTime *ElementTagType `json:"earliestStartTime,omitempty"` - LatestEndTime *ElementTagType `json:"latestEndTime,omitempty"` - MinDuration *ElementTagType `json:"minDuration,omitempty"` - MaxDuration *ElementTagType `json:"maxDuration,omitempty"` - OptionalSlot *ElementTagType `json:"optionalSlot,omitempty"` -} - -type PowerTimeSlotScheduleConstraintsListDataType struct { - PowerTimeSlotScheduleConstraintsData []PowerTimeSlotScheduleConstraintsDataType `json:"powerTimeSlotScheduleConstraintsData,omitempty"` -} - -type PowerTimeSlotScheduleConstraintsListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` - SlotNumber *PowerTimeSlotNumberType `json:"slotNumber,omitempty"` -} - -type PowerSequenceAlternativesRelationDataType struct { - AlternativeId *AlternativesIdType `json:"alternativeId,omitempty" eebus:"key"` - SequenceId []PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type PowerSequenceAlternativesRelationDataElementsType struct { - AlternativeId *ElementTagType `json:"alternativeId,omitempty"` - SequenceId *ElementTagType `json:"sequenceId,omitempty"` -} - -type PowerSequenceAlternativesRelationListDataType struct { - PowerSequenceAlternativesRelationData []PowerSequenceAlternativesRelationDataType `json:"powerSequenceAlternativesRelationData,omitempty"` -} - -type PowerSequenceAlternativesRelationListDataSelectorsType struct { - AlternativeId *AlternativesIdType `json:"alternativeId,omitempty"` - SequenceId []PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type PowerSequenceDescriptionDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - Description *DescriptionType `json:"description,omitempty"` - PositiveEnergyDirection *EnergyDirectionType `json:"positiveEnergyDirection,omitempty"` - PowerUnit *UnitOfMeasurementType `json:"powerUnit,omitempty"` - EnergyUnit *UnitOfMeasurementType `json:"energyUnit,omitempty"` - ValueSource *MeasurementValueSourceType `json:"valueSource,omitempty"` - Scope *PowerSequenceScopeType `json:"scope,omitempty"` - TaskIdentifier *uint `json:"taskIdentifier,omitempty"` - RepetitionsTotal *uint `json:"repetitionsTotal,omitempty"` -} - -type PowerSequenceDescriptionDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - Description *ElementTagType `json:"description,omitempty"` - PositiveEnergyDirection *ElementTagType `json:"positiveEnergyDirection,omitempty"` - PowerUnit *ElementTagType `json:"powerUnit,omitempty"` - EnergyUnit *ElementTagType `json:"energyUnit,omitempty"` - ValueSource *ElementTagType `json:"valueSource,omitempty"` - Scope *ElementTagType `json:"scope,omitempty"` - TaskIdentifier *ElementTagType `json:"taskIdentifier,omitempty"` - RepetitionsTotal *ElementTagType `json:"repetitionsTotal,omitempty"` -} - -type PowerSequenceDescriptionListDataType struct { - PowerSequenceDescriptionData []PowerSequenceDescriptionDataType `json:"powerSequenceDescriptionData,omitempty"` -} - -type PowerSequenceDescriptionListDataSelectorsType struct { - SequenceId []PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type PowerSequenceStateDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - State *PowerSequenceStateType `json:"state,omitempty"` - ActiveSlotNumber *PowerTimeSlotNumberType `json:"activeSlotNumber,omitempty"` - ElapsedSlotTime *DurationType `json:"elapsedSlotTime,omitempty"` - RemainingSlotTime *DurationType `json:"remainingSlotTime,omitempty"` - SequenceRemoteControllable *bool `json:"sequenceRemoteControllable,omitempty"` - ActiveRepetitionNumber *uint `json:"activeRepetitionNumber,omitempty"` - RemainingPauseTime *DurationType `json:"remainingPauseTime,omitempty"` -} - -type PowerSequenceStateDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - State *ElementTagType `json:"state,omitempty"` - ActiveSlotNumber *ElementTagType `json:"activeSlotNumber,omitempty"` - ElapsedSlotTime *ElementTagType `json:"elapsedSlotTime,omitempty"` - RemainingSlotTime *ElementTagType `json:"remainingSlotTime,omitempty"` - SequenceRemoteControllable *ElementTagType `json:"sequenceRemoteControllable,omitempty"` - ActiveRepetitionNumber *ElementTagType `json:"activeRepetitionNumber,omitempty"` - RemainingPauseTime *ElementTagType `json:"remainingPauseTime,omitempty"` -} - -type PowerSequenceStateListDataType struct { - PowerSequenceStateData []PowerSequenceStateDataType `json:"powerSequenceStateData,omitempty"` -} - -type PowerSequenceStateListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type PowerSequenceScheduleDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - StartTime *AbsoluteOrRelativeTimeType `json:"startTime,omitempty"` - EndTime *AbsoluteOrRelativeTimeType `json:"endTime,omitempty"` -} - -type PowerSequenceScheduleDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - StartTime *ElementTagType `json:"startTime,omitempty"` - EndTime *ElementTagType `json:"endTime,omitempty"` -} - -type PowerSequenceScheduleListDataType struct { - PowerSequenceScheduleData []PowerSequenceScheduleDataType `json:"powerSequenceScheduleData,omitempty"` -} - -type PowerSequenceScheduleListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type PowerSequenceScheduleConstraintsDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - EarliestStartTime *AbsoluteOrRelativeTimeType `json:"earliestStartTime,omitempty"` - LatestStartTime *AbsoluteOrRelativeTimeType `json:"latestStartTime,omitempty"` - EarliestEndTime *AbsoluteOrRelativeTimeType `json:"earliestEndTime,omitempty"` - LatestEndTime *AbsoluteOrRelativeTimeType `json:"latestEndTime,omitempty"` - OptionalSequence *bool `json:"optionalSequence,omitempty"` -} - -type PowerSequenceScheduleConstraintsDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - EarliestStartTime *ElementTagType `json:"earliestStartTime,omitempty"` - LatestStartTime *ElementTagType `json:"latestStartTime,omitempty"` - EarliestEndTime *ElementTagType `json:"earliestEndTime,omitempty"` - LatestEndTime *ElementTagType `json:"latestEndTime,omitempty"` - OptionalSequence *ElementTagType `json:"optionalSequence,omitempty"` -} - -type PowerSequenceScheduleConstraintsListDataType struct { - PowerSequenceScheduleConstraintsData []PowerSequenceScheduleConstraintsDataType `json:"powerSequenceScheduleConstraintsData,omitempty"` -} - -type PowerSequenceScheduleConstraintsListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type PowerSequencePriceDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - PotentialStartTime *AbsoluteOrRelativeTimeType `json:"potentialStartTime,omitempty"` - Price *ScaledNumberType `json:"price,omitempty"` - Currency *CurrencyType `json:"currency,omitempty"` -} - -type PowerSequencePriceDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - PotentialStartTime *ElementTagType `json:"potentialStartTime,omitempty"` - Price *ElementTagType `json:"price,omitempty"` - Currency *ElementTagType `json:"currency,omitempty"` -} - -type PowerSequencePriceListDataType struct { - PowerSequencePriceData []PowerSequencePriceDataType `json:"powerSequencePriceData,omitempty"` -} - -type PowerSequencePriceListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` - PotentialStartTimeInterval *AbsoluteOrRelativeTimeType `json:"potentialStartTimeInterval,omitempty"` -} - -type PowerSequenceSchedulePreferenceDataType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty" eebus:"key"` - Greenest *bool `json:"greenest,omitempty"` - Cheapest *bool `json:"cheapest,omitempty"` -} - -type PowerSequenceSchedulePreferenceDataElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - Greenest *ElementTagType `json:"greenest,omitempty"` - Cheapest *ElementTagType `json:"cheapest,omitempty"` -} - -type PowerSequenceSchedulePreferenceListDataType struct { - PowerSequenceSchedulePreferenceData []PowerSequenceSchedulePreferenceDataType `json:"powerSequenceSchedulePreferenceData,omitempty"` -} - -type PowerSequenceSchedulePreferenceListDataSelectorsType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type PowerSequenceNodeScheduleInformationDataType struct { - NodeRemoteControllable *bool `json:"nodeRemoteControllable,omitempty"` - SupportsSingleSlotSchedulingOnly *bool `json:"supportsSingleSlotSchedulingOnly,omitempty"` - AlternativesCount *uint `json:"alternativesCount,omitempty"` - TotalSequencesCountMax *uint `json:"totalSequencesCountMax,omitempty"` - SupportsReselection *bool `json:"supportsReselection,omitempty"` -} - -type PowerSequenceNodeScheduleInformationDataElementsType struct { - NodeRemoteControllable *ElementTagType `json:"nodeRemoteControllable,omitempty"` - SupportsSingleSlotSchedulingOnly *ElementTagType `json:"supportsSingleSlotSchedulingOnly,omitempty"` - AlternativesCount *ElementTagType `json:"alternativesCount,omitempty"` - TotalSequencesCountMax *ElementTagType `json:"totalSequencesCountMax,omitempty"` - SupportsReselection *ElementTagType `json:"supportsReselection,omitempty"` -} - -type PowerSequenceScheduleConfigurationRequestCallType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type PowerSequenceScheduleConfigurationRequestCallElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` -} - -type PowerSequencePriceCalculationRequestCallType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` - PotentialStartTime *AbsoluteOrRelativeTimeType `json:"potentialStartTime,omitempty"` -} - -type PowerSequencePriceCalculationRequestCallElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` - PotentialStartTime *ElementTagType `json:"potentialStartTime,omitempty"` -} diff --git a/spine/model/powersequences_additions.go b/spine/model/powersequences_additions.go deleted file mode 100644 index f2329597..00000000 --- a/spine/model/powersequences_additions.go +++ /dev/null @@ -1,131 +0,0 @@ -package model - -// PowerTimeSlotScheduleListDataType - -var _ Updater = (*PowerTimeSlotScheduleListDataType)(nil) - -func (r *PowerTimeSlotScheduleListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerTimeSlotScheduleDataType - if newList != nil { - newData = newList.(*PowerTimeSlotScheduleListDataType).PowerTimeSlotScheduleData - } - - r.PowerTimeSlotScheduleData = UpdateList(r.PowerTimeSlotScheduleData, newData, filterPartial, filterDelete) -} - -// PowerTimeSlotValueListDataType - -var _ Updater = (*PowerTimeSlotValueListDataType)(nil) - -func (r *PowerTimeSlotValueListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerTimeSlotValueDataType - if newList != nil { - newData = newList.(*PowerTimeSlotValueListDataType).PowerTimeSlotValueData - } - - r.PowerTimeSlotValueData = UpdateList(r.PowerTimeSlotValueData, newData, filterPartial, filterDelete) -} - -// PowerTimeSlotScheduleConstraintsListDataType - -var _ Updater = (*PowerTimeSlotScheduleConstraintsListDataType)(nil) - -func (r *PowerTimeSlotScheduleConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerTimeSlotScheduleConstraintsDataType - if newList != nil { - newData = newList.(*PowerTimeSlotScheduleConstraintsListDataType).PowerTimeSlotScheduleConstraintsData - } - - r.PowerTimeSlotScheduleConstraintsData = UpdateList(r.PowerTimeSlotScheduleConstraintsData, newData, filterPartial, filterDelete) -} - -// PowerSequenceAlternativesRelationListDataType - -var _ Updater = (*PowerSequenceAlternativesRelationListDataType)(nil) - -func (r *PowerSequenceAlternativesRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerSequenceAlternativesRelationDataType - if newList != nil { - newData = newList.(*PowerSequenceAlternativesRelationListDataType).PowerSequenceAlternativesRelationData - } - - r.PowerSequenceAlternativesRelationData = UpdateList(r.PowerSequenceAlternativesRelationData, newData, filterPartial, filterDelete) -} - -// PowerSequenceDescriptionListDataType - -var _ Updater = (*PowerSequenceDescriptionListDataType)(nil) - -func (r *PowerSequenceDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerSequenceDescriptionDataType - if newList != nil { - newData = newList.(*PowerSequenceDescriptionListDataType).PowerSequenceDescriptionData - } - - r.PowerSequenceDescriptionData = UpdateList(r.PowerSequenceDescriptionData, newData, filterPartial, filterDelete) -} - -// PowerSequenceStateListDataType - -var _ Updater = (*PowerSequenceStateListDataType)(nil) - -func (r *PowerSequenceStateListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerSequenceStateDataType - if newList != nil { - newData = newList.(*PowerSequenceStateListDataType).PowerSequenceStateData - } - - r.PowerSequenceStateData = UpdateList(r.PowerSequenceStateData, newData, filterPartial, filterDelete) -} - -// PowerSequenceScheduleListDataType - -var _ Updater = (*PowerSequenceScheduleListDataType)(nil) - -func (r *PowerSequenceScheduleListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerSequenceScheduleDataType - if newList != nil { - newData = newList.(*PowerSequenceScheduleListDataType).PowerSequenceScheduleData - } - - r.PowerSequenceScheduleData = UpdateList(r.PowerSequenceScheduleData, newData, filterPartial, filterDelete) -} - -// PowerSequenceScheduleConstraintsListDataType - -var _ Updater = (*PowerSequenceScheduleConstraintsListDataType)(nil) - -func (r *PowerSequenceScheduleConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerSequenceScheduleConstraintsDataType - if newList != nil { - newData = newList.(*PowerSequenceScheduleConstraintsListDataType).PowerSequenceScheduleConstraintsData - } - - r.PowerSequenceScheduleConstraintsData = UpdateList(r.PowerSequenceScheduleConstraintsData, newData, filterPartial, filterDelete) -} - -// PowerSequencePriceListDataType - -var _ Updater = (*PowerSequencePriceListDataType)(nil) - -func (r *PowerSequencePriceListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerSequencePriceDataType - if newList != nil { - newData = newList.(*PowerSequencePriceListDataType).PowerSequencePriceData - } - - r.PowerSequencePriceData = UpdateList(r.PowerSequencePriceData, newData, filterPartial, filterDelete) -} - -// PowerSequenceSchedulePreferenceListDataType - -var _ Updater = (*PowerSequenceSchedulePreferenceListDataType)(nil) - -func (r *PowerSequenceSchedulePreferenceListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []PowerSequenceSchedulePreferenceDataType - if newList != nil { - newData = newList.(*PowerSequenceSchedulePreferenceListDataType).PowerSequenceSchedulePreferenceData - } - - r.PowerSequenceSchedulePreferenceData = UpdateList(r.PowerSequenceSchedulePreferenceData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/powersequences_additions_test.go b/spine/model/powersequences_additions_test.go deleted file mode 100644 index 25211bad..00000000 --- a/spine/model/powersequences_additions_test.go +++ /dev/null @@ -1,391 +0,0 @@ -package model - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestPowerTimeSlotScheduleListDataType_Update(t *testing.T) { - sut := PowerTimeSlotScheduleListDataType{ - PowerTimeSlotScheduleData: []PowerTimeSlotScheduleDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - SlotActivated: util.Ptr(false), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - SlotActivated: util.Ptr(false), - }, - }, - } - - newData := PowerTimeSlotScheduleListDataType{ - PowerTimeSlotScheduleData: []PowerTimeSlotScheduleDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - SlotActivated: util.Ptr(true), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerTimeSlotScheduleData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, false, *item1.SlotActivated) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, true, *item2.SlotActivated) -} - -func TestPowerTimeSlotValueListDataType_Update(t *testing.T) { - sut := PowerTimeSlotValueListDataType{ - PowerTimeSlotValueData: []PowerTimeSlotValueDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - Value: NewScaledNumberType(1), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - Value: NewScaledNumberType(1), - }, - }, - } - - newData := PowerTimeSlotValueListDataType{ - PowerTimeSlotValueData: []PowerTimeSlotValueDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - Value: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerTimeSlotValueData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, 1.0, item1.Value.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, 10.0, item2.Value.GetValue()) -} - -func TestPowerTimeSlotScheduleConstraintsListDataType_Update(t *testing.T) { - sut := PowerTimeSlotScheduleConstraintsListDataType{ - PowerTimeSlotScheduleConstraintsData: []PowerTimeSlotScheduleConstraintsDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - MinDuration: NewDurationType(1 * time.Second), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - MinDuration: NewDurationType(1 * time.Second), - }, - }, - } - - newData := PowerTimeSlotScheduleConstraintsListDataType{ - PowerTimeSlotScheduleConstraintsData: []PowerTimeSlotScheduleConstraintsDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - MinDuration: NewDurationType(10 * time.Second), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerTimeSlotScheduleConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - duration, _ := item1.MinDuration.GetTimeDuration() - assert.Equal(t, time.Duration(1*time.Second), duration) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - duration, _ = item2.MinDuration.GetTimeDuration() - assert.Equal(t, time.Duration(10*time.Second), duration) -} - -func TestPowerSequenceAlternativesRelationListDataType_Update(t *testing.T) { - sut := PowerSequenceAlternativesRelationListDataType{ - PowerSequenceAlternativesRelationData: []PowerSequenceAlternativesRelationDataType{ - { - AlternativeId: util.Ptr(AlternativesIdType(0)), - SequenceId: []PowerSequenceIdType{0}, - }, - { - AlternativeId: util.Ptr(AlternativesIdType(1)), - SequenceId: []PowerSequenceIdType{0}, - }, - }, - } - - newData := PowerSequenceAlternativesRelationListDataType{ - PowerSequenceAlternativesRelationData: []PowerSequenceAlternativesRelationDataType{ - { - AlternativeId: util.Ptr(AlternativesIdType(1)), - SequenceId: []PowerSequenceIdType{1}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerSequenceAlternativesRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.AlternativeId)) - assert.Equal(t, 0, int(item1.SequenceId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.AlternativeId)) - assert.Equal(t, 1, int(item2.SequenceId[0])) -} - -func TestPowerSequenceDescriptionListDataType_Update(t *testing.T) { - sut := PowerSequenceDescriptionListDataType{ - PowerSequenceDescriptionData: []PowerSequenceDescriptionDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeConsume), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeConsume), - }, - }, - } - - newData := PowerSequenceDescriptionListDataType{ - PowerSequenceDescriptionData: []PowerSequenceDescriptionDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - PositiveEnergyDirection: util.Ptr(EnergyDirectionTypeProduce), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerSequenceDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, EnergyDirectionTypeConsume, *item1.PositiveEnergyDirection) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, EnergyDirectionTypeProduce, *item2.PositiveEnergyDirection) -} - -func TestPowerSequenceStateListDataType_Update(t *testing.T) { - sut := PowerSequenceStateListDataType{ - PowerSequenceStateData: []PowerSequenceStateDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - State: util.Ptr(PowerSequenceStateTypeRunning), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - State: util.Ptr(PowerSequenceStateTypeRunning), - }, - }, - } - - newData := PowerSequenceStateListDataType{ - PowerSequenceStateData: []PowerSequenceStateDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - State: util.Ptr(PowerSequenceStateTypeCompleted), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerSequenceStateData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, PowerSequenceStateTypeRunning, *item1.State) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, PowerSequenceStateTypeCompleted, *item2.State) -} - -func TestPowerSequenceScheduleListDataType_Update(t *testing.T) { - sut := PowerSequenceScheduleListDataType{ - PowerSequenceScheduleData: []PowerSequenceScheduleDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - EndTime: NewAbsoluteOrRelativeTimeType("PT2H"), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - EndTime: NewAbsoluteOrRelativeTimeType("PT2H"), - }, - }, - } - - newData := PowerSequenceScheduleListDataType{ - PowerSequenceScheduleData: []PowerSequenceScheduleDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - EndTime: NewAbsoluteOrRelativeTimeType("PT4H"), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerSequenceScheduleData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, "PT2H", string(*item1.EndTime)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, "PT4H", string(*item2.EndTime)) -} - -func TestPowerSequenceScheduleConstraintsListDataType_Update(t *testing.T) { - sut := PowerSequenceScheduleConstraintsListDataType{ - PowerSequenceScheduleConstraintsData: []PowerSequenceScheduleConstraintsDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - EarliestEndTime: NewAbsoluteOrRelativeTimeType("PT2H"), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - EarliestEndTime: NewAbsoluteOrRelativeTimeType("PT2H"), - }, - }, - } - - newData := PowerSequenceScheduleConstraintsListDataType{ - PowerSequenceScheduleConstraintsData: []PowerSequenceScheduleConstraintsDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - EarliestEndTime: NewAbsoluteOrRelativeTimeType("PT4H"), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerSequenceScheduleConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, "PT2H", string(*item1.EarliestEndTime)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, "PT4H", string(*item2.EarliestEndTime)) -} - -func TestPowerSequencePriceListDataType_Update(t *testing.T) { - sut := PowerSequencePriceListDataType{ - PowerSequencePriceData: []PowerSequencePriceDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - Price: NewScaledNumberType(1), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - Price: NewScaledNumberType(1), - }, - }, - } - - newData := PowerSequencePriceListDataType{ - PowerSequencePriceData: []PowerSequencePriceDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - Price: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerSequencePriceData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, 1.0, item1.Price.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, 10.0, item2.Price.GetValue()) -} - -func TestPowerSequenceSchedulePreferenceListDataType_Update(t *testing.T) { - sut := PowerSequenceSchedulePreferenceListDataType{ - PowerSequenceSchedulePreferenceData: []PowerSequenceSchedulePreferenceDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(0)), - Cheapest: util.Ptr(false), - }, - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - Cheapest: util.Ptr(false), - }, - }, - } - - newData := PowerSequenceSchedulePreferenceListDataType{ - PowerSequenceSchedulePreferenceData: []PowerSequenceSchedulePreferenceDataType{ - { - SequenceId: util.Ptr(PowerSequenceIdType(1)), - Cheapest: util.Ptr(true), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.PowerSequenceSchedulePreferenceData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SequenceId)) - assert.Equal(t, false, *item1.Cheapest) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SequenceId)) - assert.Equal(t, true, *item2.Cheapest) -} diff --git a/spine/model/result.go b/spine/model/result.go deleted file mode 100644 index 59c2bcd8..00000000 --- a/spine/model/result.go +++ /dev/null @@ -1,21 +0,0 @@ -package model - -type ErrorNumberType uint - -const ( - ErrorNumberTypeNoError ErrorNumberType = iota - ErrorNumberTypeGeneralError - ErrorNumberTypeTimeout - ErrorNumberTypeOverload - ErrorNumberTypeDestinationUnknown - ErrorNumberTypeDestinationUnreachable - ErrorNumberTypeCommandNotSupported - ErrorNumberTypeCommandRejected - ErrorNumberTypeRestrictedFunctionExchangeCombinationNotSupported - ErrorNumberTypeBindingIsNecessaryForThisCommand -) - -type ResultDataType struct { - ErrorNumber *ErrorNumberType `json:"errorNumber,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} diff --git a/spine/model/sensing.go b/spine/model/sensing.go deleted file mode 100644 index e19ba3d9..00000000 --- a/spine/model/sensing.go +++ /dev/null @@ -1,99 +0,0 @@ -package model - -type SensingStateType string - -const ( - SensingStateTypeOn SensingStateType = "on" - SensingStateTypeOff SensingStateType = "off" - SensingStateTypeToggle SensingStateType = "toggle" - SensingStateTypeLevel SensingStateType = "level" - SensingStateTypeLevelUp SensingStateType = "levelUp" - SensingStateTypeLevelDown SensingStateType = "levelDown" - SensingStateTypeLevelStart SensingStateType = "levelStart" - SensingStateTypeLevelStop SensingStateType = "levelStop" - SensingStateTypeLevelAbsolute SensingStateType = "levelAbsolute" - SensingStateTypeLevelRelative SensingStateType = "levelRelative" - SensingStateTypeLevelPercentageAbsolute SensingStateType = "levelPercentageAbsolute" - SensingStateTypeLevelPercentageRelative SensingStateType = "levelPercentageRelative" - SensingStateTypePressed SensingStateType = "pressed" - SensingStateTypeLongPressed SensingStateType = "longPressed" - SensingStateTypeReleased SensingStateType = "released" - SensingStateTypeChanged SensingStateType = "changed" - SensingStateTypeStarted SensingStateType = "started" - SensingStateTypeStopped SensingStateType = "stopped" - SensingStateTypePaused SensingStateType = "paused" - SensingStateTypeMiddle SensingStateType = "middle" - SensingStateTypeUp SensingStateType = "up" - SensingStateTypeDown SensingStateType = "down" - SensingStateTypeForward SensingStateType = "forward" - SensingStateTypeBackwards SensingStateType = "backwards" - SensingStateTypeOpen SensingStateType = "open" - SensingStateTypeClosed SensingStateType = "closed" - SensingStateTypeOpening SensingStateType = "opening" - SensingStateTypeClosing SensingStateType = "closing" - SensingStateTypeHigh SensingStateType = "high" - SensingStateTypeLow SensingStateType = "low" - SensingStateTypeDay SensingStateType = "day" - SensingStateTypeNight SensingStateType = "night" - SensingStateTypeDetected SensingStateType = "detected" - SensingStateTypeNotDetected SensingStateType = "notDetected" - SensingStateTypeAlarmed SensingStateType = "alarmed" - SensingStateTypeNotAlarmed SensingStateType = "notAlarmed" -) - -type SensingTypeType string - -const ( - SensingTypeTypeSwitch SensingTypeType = "switch" - SensingTypeTypeButton SensingTypeType = "button" - SensingTypeTypeLevel SensingTypeType = "level" - SensingTypeTypeLevelSwitch SensingTypeType = "levelSwitch" - SensingTypeTypeWindowHandle SensingTypeType = "windowHandle" - SensingTypeTypeContactSensor SensingTypeType = "contactSensor" - SensingTypeTypeOccupancySensor SensingTypeType = "occupancySensor" - SensingTypeTypeMotionDetector SensingTypeType = "motionDetector" - SensingTypeTypeFireDetector SensingTypeType = "fireDetector" - SensingTypeTypeSmokeDetector SensingTypeType = "smokeDetector" - SensingTypeTypeHeatDetector SensingTypeType = "heatDetector" - SensingTypeTypeWaterDetector SensingTypeType = "waterDetector" - SensingTypeTypeGasDetector SensingTypeType = "gasDetector" - SensingTypeTypeAlarmSensor SensingTypeType = "alarmSensor" - SensingTypeTypePowerAlarmSensor SensingTypeType = "powerAlarmSensor" - SensingTypeTypeDayNightIndicator SensingTypeType = "dayNightIndicator" -) - -type SensingDataType struct { - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - State *SensingStateType `json:"state,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` -} - -type SensingDataElementsType struct { - Timestamp *ElementTagType `json:"timestamp,omitempty"` - State *ElementTagType `json:"state,omitempty"` - Value *ScaledNumberElementsType `json:"value,omitempty"` -} - -type SensingListDataType struct { - SensingData []SensingDataType `json:"sensingData,omitempty"` -} - -type SensingListDataSelectorsType struct { - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` -} - -type SensingDescriptionDataType struct { - SensingType *SensingTypeType `json:"sensingType,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type SensingDescriptionDataElementsType struct { - SensingType *ElementTagType `json:"sensingType,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} diff --git a/spine/model/sensing_additions.go b/spine/model/sensing_additions.go deleted file mode 100644 index b79094dd..00000000 --- a/spine/model/sensing_additions.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -// SensingListDataType - -var _ Updater = (*SensingListDataType)(nil) - -func (r *SensingListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SensingDataType - if newList != nil { - newData = newList.(*SensingListDataType).SensingData - } - - r.SensingData = UpdateList(r.SensingData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/setpoint.go b/spine/model/setpoint.go deleted file mode 100644 index deea5198..00000000 --- a/spine/model/setpoint.go +++ /dev/null @@ -1,98 +0,0 @@ -package model - -type SetpointIdType uint - -type SetpointTypeType string - -const ( - SetpointTypeTypeValueAbsolute SetpointTypeType = "valueAbsolute" - SetpointTypeTypeValueRelative SetpointTypeType = "valueRelative" -) - -type SetpointDataType struct { - SetpointId *SetpointIdType `json:"setpointId,omitempty" eebus:"key"` - Value *ScaledNumberType `json:"value,omitempty"` - ValueMin *ScaledNumberType `json:"valueMin,omitempty"` - ValueMax *ScaledNumberType `json:"valueMax,omitempty"` - ValueToleranceAbsolute *ScaledNumberType `json:"valueToleranceAbsolute,omitempty"` - ValueTolerancePercentage *ScaledNumberType `json:"valueTolerancePercentage,omitempty"` - IsSetpointChangeable *bool `json:"isSetpointChangeable,omitempty"` - IsSetpointActive *bool `json:"isSetpointActive,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` -} - -type SetpointDataElementsType struct { - SetpointId *ElementTagType `json:"setpointId,omitempty"` - Value *ScaledNumberElementsType `json:"value,omitempty"` - ValueMin *ScaledNumberElementsType `json:"valueMin,omitempty"` - ValueMax *ScaledNumberElementsType `json:"valueMax,omitempty"` - ValueToleranceAbsolute *ScaledNumberElementsType `json:"valueToleranceAbsolute,omitempty"` - ValueTolerancePercentage *ScaledNumberElementsType `json:"valueTolerancePercentage,omitempty"` - IsSetpointChangeable *ElementTagType `json:"isSetpointChangeable,omitempty"` - IsSetpointActive *ElementTagType `json:"isSetpointActive,omitempty"` - TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` -} - -type SetpointListDataType struct { - SetpointData []SetpointDataType `json:"setpointData,omitempty"` -} - -type SetpointListDataSelectorsType struct { - SetpointId *SetpointIdType `json:"setpointId,omitempty"` -} - -type SetpointConstraintsDataType struct { - SetpointId *SetpointIdType `json:"setpointId,omitempty" eebus:"key"` - SetpointRangeMin *ScaledNumberType `json:"setpointRangeMin,omitempty"` - SetpointRangeMax *ScaledNumberType `json:"setpointRangeMax,omitempty"` - SetpointStepSize *ScaledNumberType `json:"setpointStepSize,omitempty"` -} - -type SetpointConstraintsDataElementsType struct { - SetpointId *ElementTagType `json:"setpointId,omitempty"` - SetpointRangeMin *ScaledNumberElementsType `json:"setpointRangeMin,omitempty"` - SetpointRangeMax *ScaledNumberElementsType `json:"setpointRangeMax,omitempty"` - SetpointStepSize *ScaledNumberElementsType `json:"setpointStepSize,omitempty"` -} - -type SetpointConstraintsListDataType struct { - SetpointConstraintsData []SetpointConstraintsDataType `json:"setpointConstraintsData,omitempty"` -} - -type SetpointConstraintsListDataSelectorsType struct { - SetpointId *SetpointIdType `json:"setpointId,omitempty"` -} - -type SetpointDescriptionDataType struct { - SetpointId *SetpointIdType `json:"setpointId,omitempty" eebus:"key"` - MeasurementId *SetpointIdType `json:"measurementId,omitempty"` - TimeTableId *SetpointIdType `json:"timeTableId,omitempty"` - SetpointType *SetpointTypeType `json:"setpointType,omitempty"` - Unit *ScaledNumberType `json:"unit,omitempty"` - ScopeType *ScaledNumberType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type SetpointDescriptionDataElementsType struct { - SetpointId *ElementTagType `json:"setpointId,omitempty"` - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - TimeTableId *ElementTagType `json:"timeTableId,omitempty"` - SetpointType *ElementTagType `json:"setpointType,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type SetpointDescriptionListDataType struct { - SetpointDescriptionData []SetpointDescriptionDataType `json:"setpointDescriptionData,omitempty"` -} - -type SetpointDescriptionListDataSelectorsType struct { - SetpointId *SetpointIdType `json:"setpointId,omitempty"` - MeasurementId *SetpointIdType `json:"measurementId,omitempty"` - TimeTableId *SetpointIdType `json:"timeTableId,omitempty"` - SetpointType *SetpointIdType `json:"setpointType,omitempty"` - ScopeType *ScaledNumberType `json:"scopeType,omitempty"` -} diff --git a/spine/model/setpoint_additions.go b/spine/model/setpoint_additions.go deleted file mode 100644 index 8ab7915c..00000000 --- a/spine/model/setpoint_additions.go +++ /dev/null @@ -1,27 +0,0 @@ -package model - -// SetpointListDataType - -var _ Updater = (*SetpointListDataType)(nil) - -func (r *SetpointListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SetpointDataType - if newList != nil { - newData = newList.(*SetpointListDataType).SetpointData - } - - r.SetpointData = UpdateList(r.SetpointData, newData, filterPartial, filterDelete) -} - -// SetpointDescriptionListDataType - -var _ Updater = (*SetpointDescriptionListDataType)(nil) - -func (r *SetpointDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SetpointDescriptionDataType - if newList != nil { - newData = newList.(*SetpointDescriptionListDataType).SetpointDescriptionData - } - - r.SetpointDescriptionData = UpdateList(r.SetpointDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/setpoint_additions_test.go b/spine/model/setpoint_additions_test.go deleted file mode 100644 index 463273d2..00000000 --- a/spine/model/setpoint_additions_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestSetpointListDataType_Update(t *testing.T) { - sut := SetpointListDataType{ - SetpointData: []SetpointDataType{ - { - SetpointId: util.Ptr(SetpointIdType(0)), - Value: NewScaledNumberType(1), - }, - { - SetpointId: util.Ptr(SetpointIdType(1)), - Value: NewScaledNumberType(1), - }, - }, - } - - newData := SetpointListDataType{ - SetpointData: []SetpointDataType{ - { - SetpointId: util.Ptr(SetpointIdType(1)), - Value: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.SetpointData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SetpointId)) - assert.Equal(t, 1.0, item1.Value.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SetpointId)) - assert.Equal(t, 10.0, item2.Value.GetValue()) -} - -func TestSetpointDescriptionListDataType_Update(t *testing.T) { - sut := SetpointDescriptionListDataType{ - SetpointDescriptionData: []SetpointDescriptionDataType{ - { - SetpointId: util.Ptr(SetpointIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - SetpointId: util.Ptr(SetpointIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := SetpointDescriptionListDataType{ - SetpointDescriptionData: []SetpointDescriptionDataType{ - { - SetpointId: util.Ptr(SetpointIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.SetpointDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SetpointId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SetpointId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/smartenergymanagementps.go b/spine/model/smartenergymanagementps.go deleted file mode 100644 index 7f95c004..00000000 --- a/spine/model/smartenergymanagementps.go +++ /dev/null @@ -1,104 +0,0 @@ -package model - -type SmartEnergyManagementPsAlternativesRelationType PowerSequenceAlternativesRelationDataType // ignoring the custom changes - -type SmartEnergyManagementPsAlternativesRelationElementsType PowerSequenceAlternativesRelationDataElementsType // ignoring changes - -type SmartEnergyManagementPsAlternativesType struct { - Relation *SmartEnergyManagementPsAlternativesRelationType `json:"relation,omitempty"` - PowerSequence []SmartEnergyManagementPsPowerSequenceType `json:"powerSequence,omitempty"` -} - -type SmartEnergyManagementPsAlternativesElementsType struct { - Relation *SmartEnergyManagementPsAlternativesRelationElementsType `json:"relation,omitempty"` - PowerSequence *SmartEnergyManagementPsPowerSequenceElementsType `json:"powerSequence,omitempty"` -} - -type SmartEnergyManagementPsPowerSequenceType struct { - Description *PowerSequenceDescriptionDataType `json:"description,omitempty"` // ignoring changes - State *PowerSequenceStateDataType `json:"state,omitempty"` // ignoring changes - Schedule *PowerSequenceScheduleDataType `json:"schedule,omitempty"` // ignoring changes - ScheduleConstraints *PowerSequenceScheduleConstraintsDataType `json:"scheduleConstraints,omitempty"` // ignoring changes - SchedulePreference *PowerSequenceSchedulePreferenceDataType `json:"schedulePreference,omitempty"` // ignoring changes - OperatingConstraintsInterrupt *OperatingConstraintsInterruptDataType `json:"operatingConstraintsInterrupt,omitempty"` // ignoring changes - OperatingConstraintsDuration *OperatingConstraintsDurationDataType `json:"operatingConstraintsDuration,omitempty"` // ignoring changes - OperatingConstraintsResumeImplication *OperatingConstraintsResumeImplicationDataType `json:"operatingConstraintsResumeImplication,omitempty"` // ignoring changes - PowerTimeSlot []SmartEnergyManagementPsPowerTimeSlotType `json:"powerTimeSlot,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPowerSequenceElementsType struct { - Description *PowerSequenceDescriptionDataElementsType `json:"description,omitempty"` - State *PowerSequenceStateDataElementsType `json:"state,omitempty"` - Schedule *PowerSequenceScheduleDataElementsType `json:"schedule,omitempty"` - ScheduleConstraints *PowerSequenceScheduleConstraintsDataElementsType `json:"scheduleConstraints,omitempty"` - SchedulePreference *PowerSequenceSchedulePreferenceDataElementsType `json:"schedulePreference,omitempty"` - OperatingConstraintsInterrupt *OperatingConstraintsInterruptDataElementsType `json:"operatingConstraintsInterrupt,omitempty"` - OperatingConstraintsDuration *OperatingConstraintsDurationDataElementsType `json:"operatingConstraintsDuration,omitempty"` - OperatingConstraintsResumeImplication *OperatingConstraintsResumeImplicationDataElementsType `json:"operatingConstraintsResumeImplication,omitempty"` - PowerTimeSlot *SmartEnergyManagementPsPowerTimeSlotElementsType `json:"powerTimeSlot,omitempty"` -} - -type SmartEnergyManagementPsPowerTimeSlotType struct { - Schedule *PowerTimeSlotScheduleDataType `json:"schedule,omitempty"` // ignoring changes - ValueList *SmartEnergyManagementPsPowerTimeSlotValueListType `json:"valueList,omitempty"` - ScheduleConstraints *PowerTimeSlotScheduleConstraintsDataType `json:"scheduleConstraints,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPowerTimeSlotElementsType struct { - Schedule *PowerTimeSlotScheduleDataElementsType `json:"schedule,omitempty"` // ignoring changes - ValueList *SmartEnergyManagementPsPowerTimeSlotValueListElementsType `json:"valueList,omitempty"` - ScheduleConstraints *PowerTimeSlotScheduleConstraintsDataElementsType `json:"scheduleConstraints,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPowerTimeSlotValueListType struct { - Value []PowerTimeSlotValueDataType `json:"value,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPowerTimeSlotValueListElementsType struct { - Value *PowerTimeSlotValueDataElementsType `json:"value,omitempty"` -} - -type SmartEnergyManagementPsDataType struct { - NodeScheduleInformation *PowerSequenceNodeScheduleInformationDataType `json:"nodeScheduleInformation,omitempty"` // ignoring changes - Alternatives []SmartEnergyManagementPsAlternativesType `json:"alternatives,omitempty"` -} - -type SmartEnergyManagementPsDataElementsType struct { - NodeScheduleInformation *PowerSequenceNodeScheduleInformationDataElementsType `json:"nodeScheduleInformation,omitempty"` - Alternatives *SmartEnergyManagementPsAlternativesElementsType `json:"alternatives,omitempty"` -} - -type SmartEnergyManagementPsDataSelectorsType struct { - AlternativesRelation *PowerSequenceAlternativesRelationListDataSelectorsType `json:"alternativesRelation,omitempty"` // ignoring changes - PowerSequenceDescription *PowerSequenceDescriptionListDataSelectorsType `json:"powerSequenceDescription,omitempty"` // ignoring changes - PowerTimeSlotSchedule *PowerTimeSlotScheduleListDataSelectorsType `json:"powerTimeSlotSchedule,omitempty"` // ignoring changes - PowerTimeSlotValue *PowerTimeSlotValueListDataSelectorsType `json:"powerTimeSlotValue,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPriceDataType struct { - Price *PowerSequencePriceDataType `json:"price,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPriceDataElementsType struct { - Price *PowerSequencePriceDataElementsType `json:"price,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPriceDataSelectorsType struct { - Price *PowerSequencePriceListDataSelectorsType `json:"price,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsConfigurationRequestCallType struct { - ScheduleConfigurationRequest *PowerSequenceScheduleConfigurationRequestCallType `json:"scheduleConfigurationRequest,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsConfigurationRequestCallElementsType struct { - ScheduleConfigurationRequest *PowerSequenceScheduleConfigurationRequestCallElementsType `json:"scheduleConfigurationRequest,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPriceCalculationRequestCallType struct { - PriceCalculationRequest *PowerSequencePriceCalculationRequestCallType `json:"priceCalculationRequest,omitempty"` // ignoring changes -} - -type SmartEnergyManagementPsPriceCalculationRequestCallElementsType struct { - PriceCalculationRequest *PowerSequencePriceCalculationRequestCallElementsType `json:"priceCalculationRequest,omitempty"` // ignoring changes -} diff --git a/spine/model/stateinformation.go b/spine/model/stateinformation.go deleted file mode 100644 index 795f303c..00000000 --- a/spine/model/stateinformation.go +++ /dev/null @@ -1,115 +0,0 @@ -package model - -type StateInformationIdType uint - -type StateInformationType string - -// union StateInformationFunctionalityType StateInformationFunctionalityType -const ( - StateInformationTypeExternalOverrideFromGrid StateInformationType = "externalOverrideFromGrid" - StateInformationTypeAutonomousGridSupport StateInformationType = "autonomousGridSupport" - StateInformationTypeIslandingMode StateInformationType = "islandingMode" - StateInformationTypeBalancing StateInformationType = "balancing" - StateInformationTypeTrickleCharging StateInformationType = "trickleCharging" - StateInformationTypeCalibration StateInformationType = "calibration" - StateInformationTypeCommissioningMissing StateInformationType = "commissioningMissing" - StateInformationTypeSleeping StateInformationType = "sleeping" - StateInformationTypeStarting StateInformationType = "starting" - StateInformationTypeMppt StateInformationType = "mppt" - StateInformationTypeThrottled StateInformationType = "throttled" - StateInformationTypeShuttingDown StateInformationType = "shuttingDown" - StateInformationTypeManualShutdown StateInformationType = "manualShutdown" - StateInformationTypeInverterDefective StateInformationType = "inverterDefective" - StateInformationTypeBatteryOvercurrentProtection StateInformationType = "batteryOvercurrentProtection" - StateInformationTypePvStringOvercurrentProtection StateInformationType = "pvStringOvercurrentProtection" - StateInformationTypeGridFault StateInformationType = "gridFault" - StateInformationTypeGroundFault StateInformationType = "groundFault" - StateInformationTypeAcDisconnected StateInformationType = "acDisconnected" - StateInformationTypeDcDisconnected StateInformationType = "dcDisconnected" - StateInformationTypeCabinetOpen StateInformationType = "cabinetOpen" - StateInformationTypeOverTemperature StateInformationType = "overTemperature" - StateInformationTypeUnderTemperature StateInformationType = "underTemperature" - StateInformationTypeFrequencyAboveLimit StateInformationType = "frequencyAboveLimit" - StateInformationTypeFrequencyBelowLimit StateInformationType = "frequencyBelowLimit" - StateInformationTypeAcVoltageAboveLimit StateInformationType = "acVoltageAboveLimit" - StateInformationTypeAcVoltageBelowLimit StateInformationType = "acVoltageBelowLimit" - StateInformationTypeDcVoltageAboveLimit StateInformationType = "dcVoltageAboveLimit" - StateInformationTypeDcVoltageBelowLimit StateInformationType = "dcVoltageBelowLimit" - StateInformationTypeHardwareTestFailure StateInformationType = "hardwareTestFailure" - StateInformationTypeGenericInternalError StateInformationType = "genericInternalError" -) - -type StateInformationFunctionalityType string - -const ( - StateInformationFunctionalityTypeExternalOverrideFromGrid StateInformationFunctionalityType = "externalOverrideFromGrid" - StateInformationFunctionalityTypeAutonomousGridSupport StateInformationFunctionalityType = "autonomousGridSupport" - StateInformationFunctionalityTypeIslandingMode StateInformationFunctionalityType = "islandingMode" - StateInformationFunctionalityTypeBalancing StateInformationFunctionalityType = "balancing" - StateInformationFunctionalityTypeTrickleCharging StateInformationFunctionalityType = "trickleCharging" - StateInformationFunctionalityTypeCalibration StateInformationFunctionalityType = "calibration" - StateInformationFunctionalityTypeCommissioningMissing StateInformationFunctionalityType = "commissioningMissing" - StateInformationFunctionalityTypeSleeping StateInformationFunctionalityType = "sleeping" - StateInformationFunctionalityTypeStarting StateInformationFunctionalityType = "starting" - StateInformationFunctionalityTypeMppt StateInformationFunctionalityType = "mppt" - StateInformationFunctionalityTypeThrottled StateInformationFunctionalityType = "throttled" - StateInformationFunctionalityTypeShuttingDown StateInformationFunctionalityType = "shuttingDown" - StateInformationFunctionalityTypeManualShutdown StateInformationFunctionalityType = "manualShutdown" -) - -type StateInformationFailureType string - -const ( - StateInformationFailureTypeInverterDefective StateInformationFailureType = "inverterDefective" - StateInformationFailureTypeBatteryOvercurrentProtection StateInformationFailureType = "batteryOvercurrentProtection" - StateInformationFailureTypePvStringOvercurrentProtection StateInformationFailureType = "pvStringOvercurrentProtection" - StateInformationFailureTypeGridFault StateInformationFailureType = "gridFault" - StateInformationFailureTypeGroundFault StateInformationFailureType = "groundFault" - StateInformationFailureTypeAcDisconnected StateInformationFailureType = "acDisconnected" - StateInformationFailureTypeDcDisconnected StateInformationFailureType = "dcDisconnected" - StateInformationFailureTypeCabinetOpen StateInformationFailureType = "cabinetOpen" - StateInformationFailureTypeOverTemperature StateInformationFailureType = "overTemperature" - StateInformationFailureTypeUnderTemperature StateInformationFailureType = "underTemperature" - StateInformationFailureTypeFrequencyAboveLimit StateInformationFailureType = "frequencyAboveLimit" - StateInformationFailureTypeFrequencyBelowLimit StateInformationFailureType = "frequencyBelowLimit" - StateInformationFailureTypeAcVoltageAboveLimit StateInformationFailureType = "acVoltageAboveLimit" - StateInformationFailureTypeAcVoltageBelowLimit StateInformationFailureType = "acVoltageBelowLimit" - StateInformationFailureTypeDcVoltageAboveLimit StateInformationFailureType = "dcVoltageAboveLimit" - StateInformationFailureTypeDcVoltageBelowLimit StateInformationFailureType = "dcVoltageBelowLimit" - StateInformationFailureTypeHardwareTestFailure StateInformationFailureType = "hardwareTestFailure" - StateInformationFailureTypeGenericInternalError StateInformationFailureType = "genericInternalError" -) - -type StateInformationCategoryType string - -const ( - StateInformationCategoryTypeFunctionality StateInformationCategoryType = "functionality" - StateInformationCategoryTypeFailure StateInformationCategoryType = "failure" -) - -type StateInformationDataType struct { - StateInformationId *StateInformationIdType `json:"stateInformationId,omitempty" eebus:"key"` - StateInformation *StateInformationType `json:"stateInformation,omitempty"` - IsActive *bool `json:"isActive,omitempty"` - Category *StateInformationCategoryType `json:"category,omitempty"` - TimeOfLastChange *AbsoluteOrRelativeTimeType `json:"timeOfLastChange,omitempty"` -} - -type StateInformationDataElementsType struct { - StateInformationId *ElementTagType `json:"stateInformationId,omitempty"` - StateInformation *ElementTagType `json:"stateInformation,omitempty"` - IsActive *ElementTagType `json:"isActive,omitempty"` - Category *ElementTagType `json:"category,omitempty"` - TimeOfLastChange *ElementTagType `json:"timeOfLastChange,omitempty"` -} - -type StateInformationListDataType struct { - StateInformationData []StateInformationDataType `json:"stateInformationData,omitempty"` -} - -type StateInformationListDataSelectorsType struct { - StateInformationId *StateInformationIdType `json:"stateInformationId,omitempty"` - StateInformation *StateInformationType `json:"stateInformation,omitempty"` - IsActive *bool `json:"isActive,omitempty"` - Category *StateInformationCategoryType `json:"category,omitempty"` -} diff --git a/spine/model/stateinformation_additions.go b/spine/model/stateinformation_additions.go deleted file mode 100644 index 458e4970..00000000 --- a/spine/model/stateinformation_additions.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -// StateInformationListDataType - -var _ Updater = (*StateInformationListDataType)(nil) - -func (r *StateInformationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []StateInformationDataType - if newList != nil { - newData = newList.(*StateInformationListDataType).StateInformationData - } - - r.StateInformationData = UpdateList(r.StateInformationData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/subscriptionmanagement.go b/spine/model/subscriptionmanagement.go deleted file mode 100644 index d5714004..00000000 --- a/spine/model/subscriptionmanagement.go +++ /dev/null @@ -1,53 +0,0 @@ -package model - -type SubscriptionIdType uint - -type SubscriptionManagementEntryDataType struct { - SubscriptionId *SubscriptionIdType `json:"subscriptionId,omitempty" eebus:"key"` - ClientAddress *FeatureAddressType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressType `json:"serverAddress,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type SubscriptionManagementEntryDataElementsType struct { - SubscriptionId *ElementTagType `json:"subscriptionId,omitempty"` - ClientAddress *FeatureAddressElementsType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressElementsType `json:"serverAddress,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type SubscriptionManagementEntryListDataType struct { - SubscriptionManagementEntryData []SubscriptionManagementEntryDataType `json:"subscriptionManagementEntryData,omitempty"` -} - -type SubscriptionManagementEntryListDataSelectorsType struct { - SubscriptionId *SubscriptionIdType `json:"subscriptionId,omitempty"` - ClientAddress *FeatureAddressType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressType `json:"serverAddress,omitempty"` -} - -type SubscriptionManagementRequestCallType struct { - ClientAddress *FeatureAddressType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressType `json:"serverAddress,omitempty"` - ServerFeatureType *FeatureTypeType `json:"serverFeatureType,omitempty"` -} - -type SubscriptionManagementRequestCallElementsType struct { - ClientAddress *FeatureAddressElementsType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressElementsType `json:"serverAddress,omitempty"` - ServerFeatureType *ElementTagType `json:"serverFeatureType,omitempty"` -} - -type SubscriptionManagementDeleteCallType struct { - SubscriptionId *SubscriptionIdType `json:"subscriptionId,omitempty"` - ClientAddress *FeatureAddressType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressType `json:"serverAddress,omitempty"` -} - -type SubscriptionManagementDeleteCallElementsType struct { - SubscriptionId *ElementTagType `json:"subscriptionId,omitempty"` - ClientAddress *FeatureAddressElementsType `json:"clientAddress,omitempty"` - ServerAddress *FeatureAddressElementsType `json:"serverAddress,omitempty"` -} diff --git a/spine/model/subscriptionmanagement_additions.go b/spine/model/subscriptionmanagement_additions.go deleted file mode 100644 index e923d345..00000000 --- a/spine/model/subscriptionmanagement_additions.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -// SubscriptionManagementEntryListDataType - -var _ Updater = (*SubscriptionManagementEntryListDataType)(nil) - -func (r *SubscriptionManagementEntryListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SubscriptionManagementEntryDataType - if newList != nil { - newData = newList.(*SubscriptionManagementEntryListDataType).SubscriptionManagementEntryData - } - - r.SubscriptionManagementEntryData = UpdateList(r.SubscriptionManagementEntryData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/subscriptionmanagement_additions_test.go b/spine/model/subscriptionmanagement_additions_test.go deleted file mode 100644 index 38ae2ce7..00000000 --- a/spine/model/subscriptionmanagement_additions_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestSubscriptionManagementEntryListDataType_Update(t *testing.T) { - sut := SubscriptionManagementEntryListDataType{ - SubscriptionManagementEntryData: []SubscriptionManagementEntryDataType{ - { - SubscriptionId: util.Ptr(SubscriptionIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - SubscriptionId: util.Ptr(SubscriptionIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := SubscriptionManagementEntryListDataType{ - SubscriptionManagementEntryData: []SubscriptionManagementEntryDataType{ - { - SubscriptionId: util.Ptr(SubscriptionIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.SubscriptionManagementEntryData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.SubscriptionId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.SubscriptionId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/supplyconditions.go b/spine/model/supplyconditions.go deleted file mode 100644 index 91cbf63c..00000000 --- a/spine/model/supplyconditions.go +++ /dev/null @@ -1,112 +0,0 @@ -package model - -type ConditionIdType uint - -type SupplyConditionEventTypeType string - -const ( - SupplyConditionEventTypeTypeThesholdExceeded SupplyConditionEventTypeType = "thesholdExceeded" - SupplyConditionEventTypeTypeFallenBelowThreshold SupplyConditionEventTypeType = "fallenBelowThreshold" - SupplyConditionEventTypeTypeSupplyInterrupt SupplyConditionEventTypeType = "supplyInterrupt" - SupplyConditionEventTypeTypeReleaseOfLimitations SupplyConditionEventTypeType = "releaseOfLimitations" - SupplyConditionEventTypeTypeOtherProblem SupplyConditionEventTypeType = "otherProblem" - SupplyConditionEventTypeTypeGridConditionUpdate SupplyConditionEventTypeType = "gridConditionUpdate" -) - -type SupplyConditionOriginatorType string - -const ( - SupplyConditionOriginatorTypeExternDSO SupplyConditionOriginatorType = "externDSO" - SupplyConditionOriginatorTypeExternSupplier SupplyConditionOriginatorType = "externSupplier" - SupplyConditionOriginatorTypeInternalLimit SupplyConditionOriginatorType = "internalLimit" - SupplyConditionOriginatorTypeInternalService SupplyConditionOriginatorType = "internalService" - SupplyConditionOriginatorTypeInternalUser SupplyConditionOriginatorType = "internalUser" -) - -type GridConditionType string - -const ( - GridConditionTypeConsumptionRed GridConditionType = "consumptionRed" - GridConditionTypeConsumptionYellow GridConditionType = "consumptionYellow" - GridConditionTypeGood GridConditionType = "good" - GridConditionTypeProductionRed GridConditionType = "productionRed" - GridConditionTypeProductionYellow GridConditionType = "productionYellow" -) - -type SupplyConditionDataType struct { - ConditionId *ConditionIdType `json:"conditionId,omitempty" eebus:"key"` - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - EventType *SupplyConditionEventTypeType `json:"eventType,omitempty"` - Originator *SupplyConditionOriginatorType `json:"originator,omitempty"` - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty"` - ThresholdPercentage *ScaledNumberType `json:"thresholdPercentage,omitempty"` - RelevantPeriod *TimePeriodType `json:"relevantPeriod,omitempty"` - Description *DescriptionType `json:"description,omitempty"` - GridCondition *GridConditionType `json:"gridCondition,omitempty"` -} - -type SupplyConditionDataElementsType struct { - ConditionId *ElementTagType `json:"conditionId,omitempty"` - Timestamp *ElementTagType `json:"timestamp,omitempty"` - EventType *ElementTagType `json:"eventType,omitempty"` - Originator *ElementTagType `json:"originator,omitempty"` - ThresholdId *ElementTagType `json:"thresholdId,omitempty"` - ThresholdPercentage *ScaledNumberElementsType `json:"thresholdPercentage,omitempty"` - RelevantPeriod *TimePeriodElementsType `json:"relevantPeriod,omitempty"` - Description *ElementTagType `json:"description,omitempty"` - GridCondition *ElementTagType `json:"gridCondition,omitempty"` -} - -type SupplyConditionListDataType struct { - SupplyConditionData []SupplyConditionDataType `json:"supplyConditionData,omitempty"` -} - -type SupplyConditionListDataSelectorsType struct { - ConditionId *ConditionIdType `json:"conditionId,omitempty"` - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` - EventType *SupplyConditionEventTypeType `json:"eventType,omitempty"` - Originator *SupplyConditionOriginatorType `json:"originator,omitempty"` -} - -type SupplyConditionDescriptionDataType struct { - ConditionId *ConditionIdType `json:"conditionId,omitempty" eebus:"key"` - CommodityType *CommodityTypeType `json:"commodityType,omitempty"` - PositiveEnergyDirection *EnergyDirectionType `json:"positiveEnergyDirection,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type SupplyConditionDescriptionDataElementsType struct { - ConditionId *ElementTagType `json:"conditionId,omitempty"` - CommodityType *ElementTagType `json:"commodityType,omitempty"` - PositiveEnergyDirection *ElementTagType `json:"positiveEnergyDirection,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type SupplyConditionDescriptionListDataType struct { - SupplyConditionDescriptionData []SupplyConditionDescriptionDataType `json:"supplyConditionDescriptionData,omitempty"` -} - -type SupplyConditionDescriptionListDataSelectorsType struct { - ConditionId *ConditionIdType `json:"conditionId,omitempty"` -} - -type SupplyConditionThresholdRelationDataType struct { - ConditionId *ConditionIdType `json:"conditionId,omitempty" eebus:"key"` - ThresholdId []ThresholdIdType `json:"thresholdId,omitempty"` -} - -type SupplyConditionThresholdRelationDataElementsType struct { - ConditionId *ElementTagType `json:"conditionId,omitempty"` - ThresholdId *ElementTagType `json:"thresholdId,omitempty"` -} - -type SupplyConditionThresholdRelationListDataType struct { - SupplyConditionThresholdRelationData []SupplyConditionThresholdRelationDataType `json:"SupplyConditionThresholdRelationDataType,omitempty"` -} - -type SupplyConditionThresholdRelationListDataSelectorsType struct { - ConditionId *ConditionIdType `json:"conditionId,omitempty"` - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty"` -} diff --git a/spine/model/supplyconditions_additions.go b/spine/model/supplyconditions_additions.go deleted file mode 100644 index 70aa92e8..00000000 --- a/spine/model/supplyconditions_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// SupplyConditionListDataType - -var _ Updater = (*SupplyConditionListDataType)(nil) - -func (r *SupplyConditionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SupplyConditionDataType - if newList != nil { - newData = newList.(*SupplyConditionListDataType).SupplyConditionData - } - - r.SupplyConditionData = UpdateList(r.SupplyConditionData, newData, filterPartial, filterDelete) -} - -// SupplyConditionDescriptionListDataType - -var _ Updater = (*SupplyConditionDescriptionListDataType)(nil) - -func (r *SupplyConditionDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SupplyConditionDescriptionDataType - if newList != nil { - newData = newList.(*SupplyConditionDescriptionListDataType).SupplyConditionDescriptionData - } - - r.SupplyConditionDescriptionData = UpdateList(r.SupplyConditionDescriptionData, newData, filterPartial, filterDelete) -} - -// SupplyConditionThresholdRelationListDataType - -var _ Updater = (*SupplyConditionThresholdRelationListDataType)(nil) - -func (r *SupplyConditionThresholdRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SupplyConditionThresholdRelationDataType - if newList != nil { - newData = newList.(*SupplyConditionThresholdRelationListDataType).SupplyConditionThresholdRelationData - } - - r.SupplyConditionThresholdRelationData = UpdateList(r.SupplyConditionThresholdRelationData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/supplyconditions_additions_test.go b/spine/model/supplyconditions_additions_test.go deleted file mode 100644 index d98f7af9..00000000 --- a/spine/model/supplyconditions_additions_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestSupplyConditionListDataType_Update(t *testing.T) { - sut := SupplyConditionListDataType{ - SupplyConditionData: []SupplyConditionDataType{ - { - ConditionId: util.Ptr(ConditionIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - ConditionId: util.Ptr(ConditionIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := SupplyConditionListDataType{ - SupplyConditionData: []SupplyConditionDataType{ - { - ConditionId: util.Ptr(ConditionIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.SupplyConditionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ConditionId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ConditionId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestSupplyConditionDescriptionListDataType_Update(t *testing.T) { - sut := SupplyConditionDescriptionListDataType{ - SupplyConditionDescriptionData: []SupplyConditionDescriptionDataType{ - { - ConditionId: util.Ptr(ConditionIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - ConditionId: util.Ptr(ConditionIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := SupplyConditionDescriptionListDataType{ - SupplyConditionDescriptionData: []SupplyConditionDescriptionDataType{ - { - ConditionId: util.Ptr(ConditionIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.SupplyConditionDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ConditionId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ConditionId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestSupplyConditionThresholdRelationListDataType_Update(t *testing.T) { - sut := SupplyConditionThresholdRelationListDataType{ - SupplyConditionThresholdRelationData: []SupplyConditionThresholdRelationDataType{ - { - ConditionId: util.Ptr(ConditionIdType(0)), - ThresholdId: []ThresholdIdType{0}, - }, - { - ConditionId: util.Ptr(ConditionIdType(1)), - ThresholdId: []ThresholdIdType{0}, - }, - }, - } - - newData := SupplyConditionThresholdRelationListDataType{ - SupplyConditionThresholdRelationData: []SupplyConditionThresholdRelationDataType{ - { - ConditionId: util.Ptr(ConditionIdType(1)), - ThresholdId: []ThresholdIdType{1}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.SupplyConditionThresholdRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ConditionId)) - assert.Equal(t, 0, int(item1.ThresholdId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ConditionId)) - assert.Equal(t, 1, int(item2.ThresholdId[0])) -} diff --git a/spine/model/tariffinformation.go b/spine/model/tariffinformation.go deleted file mode 100644 index 9e2155a2..00000000 --- a/spine/model/tariffinformation.go +++ /dev/null @@ -1,370 +0,0 @@ -package model - -type TariffIdType uint - -type TariffCountType TariffIdType - -type TierBoundaryIdType uint - -type TierBoundaryCountType TierBoundaryIdType - -type TierBoundaryTypeType string - -const ( - TierBoundaryTypeTypePowerBoundary TierBoundaryTypeType = "powerBoundary" - TierBoundaryTypeTypeEnergyBoundary TierBoundaryTypeType = "energyBoundary" - TierBoundaryTypeTypeCountBoundary TierBoundaryTypeType = "countBoundary" -) - -type CommodityIdType uint - -type TierIdType uint - -type TierCountType TierIdType - -type TierTypeType string - -const ( - TierTypeTypeFixedCost TierTypeType = "fixedCost" - TierTypeTypeDynamicCost TierTypeType = "dynamicCost" -) - -type IncentiveIdType uint - -type IncentiveCountType IncentiveIdType - -type IncentiveTypeType string - -const ( - IncentiveTypeTypeAbsoluteCost IncentiveTypeType = "absoluteCost" - IncentiveTypeTypeRelativeCost IncentiveTypeType = "relativeCost" - IncentiveTypeTypeRenewableEnergyPercentage IncentiveTypeType = "renewableEnergyPercentage" - IncentiveTypeTypeCo2Emission IncentiveTypeType = "co2Emission" -) - -type IncentivePriorityType uint - -type IncentiveValueTypeType string - -const ( - IncentiveValueTypeTypeValue IncentiveValueTypeType = "value" - IncentiveValueTypeTypeAverageValue IncentiveValueTypeType = "averageValue" - IncentiveValueTypeTypeMinvalue IncentiveValueTypeType = "minValue" - IncentiveValueTypeTypeMaxvalue IncentiveValueTypeType = "maxValue" -) - -type TariffOverallConstraintsDataType struct { - MaxTariffCount *TariffCountType `json:"maxTariffCount,omitempty"` - MaxBoundaryCount *TierBoundaryCountType `json:"maxBoundaryCount,omitempty"` - MaxTierCount *TierCountType `json:"maxTierCount,omitempty"` - MaxIncentiveCount *IncentiveCountType `json:"maxIncentiveCount,omitempty"` - MaxBoundariesPerTariff *TierBoundaryCountType `json:"maxBoundariesPerTariff,omitempty"` - MaxTiersPerTariff *TierCountType `json:"maxTiersPerTariff,omitempty"` - MaxBoundariesPerTier *TierBoundaryCountType `json:"maxBoundariesPerTier,omitempty"` - MaxIncentivesPerTier *IncentiveCountType `json:"maxIncentivesPerTier,omitempty"` -} - -type TariffOverallConstraintsDataElementsType struct { - MaxTariffCount *ElementTagType `json:"maxTariffCount,omitempty"` - MaxBoundaryCount *ElementTagType `json:"maxBoundaryCount,omitempty"` - MaxTierCount *ElementTagType `json:"maxTierCount,omitempty"` - MaxIncentiveCount *ElementTagType `json:"maxIncentiveCount,omitempty"` - MaxBoundariesPerTariff *ElementTagType `json:"maxBoundariesPerTariff,omitempty"` - MaxTiersPerTariff *ElementTagType `json:"maxTiersPerTariff,omitempty"` - MaxBoundariesPerTier *ElementTagType `json:"maxBoundariesPerTier,omitempty"` - MaxIncentivesPerTier *ElementTagType `json:"maxIncentivesPerTier,omitempty"` -} - -type TariffDataType struct { - TariffId *TariffIdType `json:"tariffId,omitempty" eebus:"key"` - ActiveTierId []TierIdType `json:"activeTierId,omitempty"` -} - -type TariffDataElementsType struct { - TariffId *ElementTagType `json:"tariffId,omitempty"` - ActiveTierId *ElementTagType `json:"activeTierId,omitempty"` -} - -type TariffListDataType struct { - TariffData []TariffDataType `json:"tariffData,omitempty"` -} - -type TariffListDataSelectorsType struct { - TariffId *TariffIdType `json:"tariffId,omitempty"` - ActiveTierId *TierIdType `json:"activeTierId,omitempty"` -} - -type TariffTierRelationDataType struct { - TariffId *TariffIdType `json:"tariffId,omitempty" eebus:"key"` - TierId []TierIdType `json:"tierId,omitempty"` -} - -type TariffTierRelationDataElementsType struct { - TariffId *ElementTagType `json:"tariffId,omitempty"` - TierId *ElementTagType `json:"tierId,omitempty"` -} - -type TariffTierRelationListDataType struct { - TariffTierRelationData []TariffTierRelationDataType `json:"tariffTierRelationData,omitempty"` -} - -type TariffTierRelationListDataSelectorsType struct { - TariffId *TariffIdType `json:"tariffId,omitempty"` - TierId *TierIdType `json:"tierId,omitempty"` -} - -type TariffBoundaryRelationDataType struct { - TariffId *TariffIdType `json:"tariffId,omitempty" eebus:"key"` - BoundaryId []TierBoundaryIdType `json:"boundaryId,omitempty"` -} - -type TariffBoundaryRelationDataElementsType struct { - TariffId *ElementTagType `json:"tariffId,omitempty"` - BoundaryId *ElementTagType `json:"boundaryId,omitempty"` -} - -type TariffBoundaryRelationListDataType struct { - TariffBoundaryRelationData []TariffBoundaryRelationDataType `json:"tariffBoundaryRelationData,omitempty"` -} - -type TariffBoundaryRelationListDataSelectorsType struct { - TariffId *TariffIdType `json:"tariffId,omitempty"` - BoundaryId *TierBoundaryIdType `json:"boundaryId,omitempty"` -} - -type TariffDescriptionDataType struct { - TariffId *TariffIdType `json:"tariffId,omitempty" eebus:"key"` - CommodityId *CommodityIdType `json:"commodityId,omitempty"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - TariffWriteable *bool `json:"tariffWriteable,omitempty"` - UpdateRequired *bool `json:"updateRequired,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` - SlotIdSupport *bool `json:"slotIdSupport,omitempty"` -} - -type TariffDescriptionDataElementsType struct { - TariffId *ElementTagType `json:"tariffId,omitempty"` - CommodityId *ElementTagType `json:"commodityId,omitempty"` - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - TariffWriteable *ElementTagType `json:"tariffWriteable,omitempty"` - UpdateRequired *ElementTagType `json:"updateRequired,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` - SlotIdSupport *ElementTagType `json:"slotIdSupport,omitempty"` -} - -type TariffDescriptionListDataType struct { - TariffDescriptionData []TariffDescriptionDataType `json:"tariffDescriptionData,omitempty"` -} - -type TariffDescriptionListDataSelectorsType struct { - TariffId *TariffIdType `json:"tariffId,omitempty"` - CommodityId *CommodityIdType `json:"commodityId,omitempty"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} - -type TierBoundaryDataType struct { - BoundaryId *TierBoundaryIdType `json:"boundaryId,omitempty" eebus:"key"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty"` - LowerBoundaryValue *ScaledNumberType `json:"lowerBoundaryValue,omitempty"` - UpperBoundaryValue *ScaledNumberType `json:"upperBoundaryValue,omitempty"` -} - -type TierBoundaryDataElementsType struct { - BoundaryId *ElementTagType `json:"boundaryId,omitempty"` - TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` - TimeTableId *ElementTagType `json:"timeTableId,omitempty"` - LowerBoundaryValue *ScaledNumberElementsType `json:"lowerBoundaryValue,omitempty"` - UpperBoundaryValue *ScaledNumberElementsType `json:"upperBoundaryValue,omitempty"` -} - -type TierBoundaryListDataType struct { - TierBoundaryData []TierBoundaryDataType `json:"tierBoundaryData,omitempty"` -} - -type TierBoundaryListDataSelectorsType struct { - BoundaryId *TierBoundaryIdType `json:"boundaryId,omitempty"` -} - -type TierBoundaryDescriptionDataType struct { - BoundaryId *TierBoundaryIdType `json:"boundaryId,omitempty" eebus:"key"` - BoundaryType *TierBoundaryTypeType `json:"boundaryType,omitempty"` - ValidForTierId *TierIdType `json:"validForTierId,omitempty"` - SwitchToTierIdWhenLower *TierIdType `json:"switchToTierIdWhenLower,omitempty"` - SwitchToTierIdWhenHigher *TierIdType `json:"switchToTierIdWhenHigher,omitempty"` - BoundaryUnit *UnitOfMeasurementType `json:"boundaryUnit,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type TierBoundaryDescriptionDataElementsType struct { - BoundaryId *ElementTagType `json:"boundaryId,omitempty"` - BoundaryType *ElementTagType `json:"boundaryType,omitempty"` - ValidForTierId *ElementTagType `json:"validForTierId,omitempty"` - SwitchToTierIdWhenLower *ElementTagType `json:"switchToTierIdWhenLower,omitempty"` - SwitchToTierIdWhenHigher *ElementTagType `json:"switchToTierIdWhenHigher,omitempty"` - BoundaryUnit *ElementTagType `json:"boundaryUnit,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type TierBoundaryDescriptionListDataType struct { - TierBoundaryDescriptionData []TierBoundaryDescriptionDataType `json:"tierBoundaryDescriptionData,omitempty"` -} - -type TierBoundaryDescriptionListDataSelectorsType struct { - BoundaryId *TierBoundaryIdType `json:"boundaryId,omitempty"` - BoundaryType *TierBoundaryTypeType `json:"boundaryType,omitempty"` -} - -type CommodityDataType struct { - CommodityId *CommodityIdType `json:"commodityId,omitempty" eebus:"key"` - CommodityType *CommodityTypeType `json:"commodityType,omitempty"` - PositiveEnergyDirection *EnergyDirectionType `json:"positiveEnergyDirection,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type CommodityDataElementsType struct { - CommodityId *ElementTagType `json:"commodityId,omitempty"` - CommodityType *ElementTagType `json:"commodityType,omitempty"` - PositiveEnergyDirection *ElementTagType `json:"positiveEnergyDirection,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type CommodityListDataType struct { - CommodityData []CommodityDataType `json:"commodityData,omitempty"` -} - -type CommodityListDataSelectorsType struct { - CommodityId *CommodityIdType `json:"commodityId,omitempty"` - CommodityType *CommodityTypeType `json:"commodityType,omitempty"` -} - -type TierDataType struct { - TierId *TierIdType `json:"tierId,omitempty" eebus:"key"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty"` - ActiveIncentiveId []IncentiveIdType `json:"activeIncentiveId,omitempty"` -} - -type TierDataElementsType struct { - TierId *ElementTagType `json:"tierId,omitempty"` - TimePeriod *ElementTagType `json:"timePeriod,omitempty"` - TimeTableId *ElementTagType `json:"timeTableId,omitempty"` - ActiveIncentiveId *ElementTagType `json:"activeIncentiveId,omitempty"` -} - -type TierListDataType struct { - TierData []TierDataType `json:"tierData,omitempty"` -} - -type TierListDataSelectorsType struct { - TierId *TierIdType `json:"tierId,omitempty"` - ActiveIncentiveId *IncentiveIdType `json:"activeIncentiveId,omitempty"` -} - -type TierIncentiveRelationDataType struct { - TierId *TierIdType `json:"tierId,omitempty" eebus:"key"` - IncentiveId []IncentiveIdType `json:"incentiveId,omitempty"` -} - -type TierIncentiveRelationDataElementsType struct { - TierId *ElementTagType `json:"tierId,omitempty"` - IncentiveId *ElementTagType `json:"incentiveId,omitempty"` -} - -type TierIncentiveRelationListDataType struct { - TierIncentiveRelationData []TierIncentiveRelationDataType `json:"tierIncentiveRelationData,omitempty"` -} - -type TierIncentiveRelationListDataSelectorsType struct { - TierId *TierIdType `json:"tierId,omitempty"` - IncentiveId *IncentiveIdType `json:"incentiveId,omitempty"` -} - -type TierDescriptionDataType struct { - TierId *TierIdType `json:"tierId,omitempty" eebus:"key"` - TierType *TierTypeType `json:"tierType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type TierDescriptionDataElementsType struct { - TierId *ElementTagType `json:"tierId,omitempty"` - TierType *ElementTagType `json:"tierType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type TierDescriptionListDataType struct { - TierDescriptionData []TierDescriptionDataType `json:"tierDescriptionData,omitempty"` -} - -type TierDescriptionListDataSelectorsType struct { - TierId *TierIdType `json:"tierId,omitempty"` - TierType *TierTypeType `json:"tierType,omitempty"` -} - -type IncentiveDataType struct { - IncentiveId *IncentiveIdType `json:"incentiveId,omitempty" eebus:"key"` - ValueType *IncentiveValueTypeType `json:"valueType,omitempty"` - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` -} - -type IncentiveDataElementsType struct { - IncentiveId *ElementTagType `json:"incentiveId,omitempty"` - ValueType *ElementTagType `json:"valueType,omitempty"` - Timestamp *ElementTagType `json:"timestamp,omitempty"` - TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` - TimeTableId *ElementTagType `json:"timeTableId,omitempty"` - Value *ScaledNumberElementsType `json:"value,omitempty"` -} - -type IncentiveListDataType struct { - IncentiveData []IncentiveDataType `json:"incentiveData,omitempty"` -} - -type IncentiveListDataSelectorsType struct { - IncentiveId *IncentiveIdType `json:"incentiveId,omitempty"` - ValueType *IncentiveValueTypeType `json:"valueType,omitempty"` - TimestampInterval *TimestampIntervalType `json:"timestampInterval,omitempty"` -} - -type IncentiveDescriptionDataType struct { - IncentiveId *IncentiveIdType `json:"incentiveId,omitempty" eebus:"key"` - IncentiveType *IncentiveTypeType `json:"incentiveType,omitempty"` - IncentivePriority *IncentivePriorityType `json:"incentivePriority,omitempty"` - Currency *CurrencyType `json:"currency,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type IncentiveDescriptionDataElementsType struct { - IncentiveId *ElementTagType `json:"incentiveId,omitempty"` - IncentiveType *ElementTagType `json:"incentiveType,omitempty"` - IncentivePriority *ElementTagType `json:"incentivePriority,omitempty"` - Currency *ElementTagType `json:"currency,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type IncentiveDescriptionListDataType struct { - IncentiveDescriptionData []IncentiveDescriptionDataType `json:"incentiveDescriptionData,omitempty"` -} - -type IncentiveDescriptionListDataSelectorsType struct { - IncentiveId *IncentiveIdType `json:"incentiveId,omitempty"` - IncentiveType *IncentiveTypeType `json:"incentiveType,omitempty"` -} diff --git a/spine/model/tariffinformation_additions.go b/spine/model/tariffinformation_additions.go deleted file mode 100644 index e05e5a70..00000000 --- a/spine/model/tariffinformation_additions.go +++ /dev/null @@ -1,157 +0,0 @@ -package model - -// TariffListDataType - -var _ Updater = (*TariffListDataType)(nil) - -func (r *TariffListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TariffDataType - if newList != nil { - newData = newList.(*TariffListDataType).TariffData - } - - r.TariffData = UpdateList(r.TariffData, newData, filterPartial, filterDelete) -} - -// TariffTierRelationListDataType - -var _ Updater = (*TariffTierRelationListDataType)(nil) - -func (r *TariffTierRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TariffTierRelationDataType - if newList != nil { - newData = newList.(*TariffTierRelationListDataType).TariffTierRelationData - } - - r.TariffTierRelationData = UpdateList(r.TariffTierRelationData, newData, filterPartial, filterDelete) -} - -// TariffBoundaryRelationListDataType - -var _ Updater = (*TariffBoundaryRelationListDataType)(nil) - -func (r *TariffBoundaryRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TariffBoundaryRelationDataType - if newList != nil { - newData = newList.(*TariffBoundaryRelationListDataType).TariffBoundaryRelationData - } - - r.TariffBoundaryRelationData = UpdateList(r.TariffBoundaryRelationData, newData, filterPartial, filterDelete) -} - -// TariffDescriptionListDataType - -var _ Updater = (*TariffDescriptionListDataType)(nil) - -func (r *TariffDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TariffDescriptionDataType - if newList != nil { - newData = newList.(*TariffDescriptionListDataType).TariffDescriptionData - } - - r.TariffDescriptionData = UpdateList(r.TariffDescriptionData, newData, filterPartial, filterDelete) -} - -// TierBoundaryListDataType - -var _ Updater = (*TierBoundaryListDataType)(nil) - -func (r *TierBoundaryListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TierBoundaryDataType - if newList != nil { - newData = newList.(*TierBoundaryListDataType).TierBoundaryData - } - - r.TierBoundaryData = UpdateList(r.TierBoundaryData, newData, filterPartial, filterDelete) -} - -// TierBoundaryDescriptionListDataType - -var _ Updater = (*TierBoundaryDescriptionListDataType)(nil) - -func (r *TierBoundaryDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TierBoundaryDescriptionDataType - if newList != nil { - newData = newList.(*TierBoundaryDescriptionListDataType).TierBoundaryDescriptionData - } - - r.TierBoundaryDescriptionData = UpdateList(r.TierBoundaryDescriptionData, newData, filterPartial, filterDelete) -} - -// CommodityListDataType - -var _ Updater = (*CommodityListDataType)(nil) - -func (r *CommodityListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []CommodityDataType - if newList != nil { - newData = newList.(*CommodityListDataType).CommodityData - } - - r.CommodityData = UpdateList(r.CommodityData, newData, filterPartial, filterDelete) -} - -// TierListDataType - -var _ Updater = (*TierListDataType)(nil) - -func (r *TierListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TierDataType - if newList != nil { - newData = newList.(*TierListDataType).TierData - } - - r.TierData = UpdateList(r.TierData, newData, filterPartial, filterDelete) -} - -// TierIncentiveRelationListDataType - -var _ Updater = (*TierIncentiveRelationListDataType)(nil) - -func (r *TierIncentiveRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TierIncentiveRelationDataType - if newList != nil { - newData = newList.(*TierIncentiveRelationListDataType).TierIncentiveRelationData - } - - r.TierIncentiveRelationData = UpdateList(r.TierIncentiveRelationData, newData, filterPartial, filterDelete) -} - -// TierDescriptionListDataType - -var _ Updater = (*TierDescriptionListDataType)(nil) - -func (r *TierDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TierDescriptionDataType - if newList != nil { - newData = newList.(*TierDescriptionListDataType).TierDescriptionData - } - - r.TierDescriptionData = UpdateList(r.TierDescriptionData, newData, filterPartial, filterDelete) -} - -// IncentiveListDataType - -var _ Updater = (*IncentiveListDataType)(nil) - -func (r *IncentiveListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []IncentiveDataType - if newList != nil { - newData = newList.(*IncentiveListDataType).IncentiveData - } - - r.IncentiveData = UpdateList(r.IncentiveData, newData, filterPartial, filterDelete) -} - -// IncentiveDescriptionListDataType - -var _ Updater = (*IncentiveDescriptionListDataType)(nil) - -func (r *IncentiveDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []IncentiveDescriptionDataType - if newList != nil { - newData = newList.(*IncentiveDescriptionListDataType).IncentiveDescriptionData - } - - r.IncentiveDescriptionData = UpdateList(r.IncentiveDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/tariffinformation_additions_test.go b/spine/model/tariffinformation_additions_test.go deleted file mode 100644 index 776168b4..00000000 --- a/spine/model/tariffinformation_additions_test.go +++ /dev/null @@ -1,464 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestTariffListDataType_Update(t *testing.T) { - sut := TariffListDataType{ - TariffData: []TariffDataType{ - { - TariffId: util.Ptr(TariffIdType(0)), - ActiveTierId: []TierIdType{0}, - }, - { - TariffId: util.Ptr(TariffIdType(1)), - ActiveTierId: []TierIdType{0}, - }, - }, - } - - newData := TariffListDataType{ - TariffData: []TariffDataType{ - { - TariffId: util.Ptr(TariffIdType(1)), - ActiveTierId: []TierIdType{1}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TariffData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TariffId)) - assert.Equal(t, 0, int(item1.ActiveTierId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TariffId)) - assert.Equal(t, 1, int(item2.ActiveTierId[0])) -} - -func TestTariffTierRelationListDataType_Update(t *testing.T) { - sut := TariffTierRelationListDataType{ - TariffTierRelationData: []TariffTierRelationDataType{ - { - TariffId: util.Ptr(TariffIdType(0)), - TierId: []TierIdType{0}, - }, - { - TariffId: util.Ptr(TariffIdType(1)), - TierId: []TierIdType{0}, - }, - }, - } - - newData := TariffTierRelationListDataType{ - TariffTierRelationData: []TariffTierRelationDataType{ - { - TariffId: util.Ptr(TariffIdType(1)), - TierId: []TierIdType{1}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TariffTierRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TariffId)) - assert.Equal(t, 0, int(item1.TierId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TariffId)) - assert.Equal(t, 1, int(item2.TierId[0])) -} - -func TestTariffBoundaryRelationListDataType_Update(t *testing.T) { - sut := TariffBoundaryRelationListDataType{ - TariffBoundaryRelationData: []TariffBoundaryRelationDataType{ - { - TariffId: util.Ptr(TariffIdType(0)), - BoundaryId: []TierBoundaryIdType{0}, - }, - { - TariffId: util.Ptr(TariffIdType(1)), - BoundaryId: []TierBoundaryIdType{0}, - }, - }, - } - - newData := TariffBoundaryRelationListDataType{ - TariffBoundaryRelationData: []TariffBoundaryRelationDataType{ - { - TariffId: util.Ptr(TariffIdType(1)), - BoundaryId: []TierBoundaryIdType{1}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TariffBoundaryRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TariffId)) - assert.Equal(t, 0, int(item1.BoundaryId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TariffId)) - assert.Equal(t, 1, int(item2.BoundaryId[0])) -} - -func TestTariffDescriptionListDataType_Update(t *testing.T) { - sut := TariffDescriptionListDataType{ - TariffDescriptionData: []TariffDescriptionDataType{ - { - TariffId: util.Ptr(TariffIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - TariffId: util.Ptr(TariffIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := TariffDescriptionListDataType{ - TariffDescriptionData: []TariffDescriptionDataType{ - { - TariffId: util.Ptr(TariffIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TariffDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TariffId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TariffId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestTierBoundaryListDataType_Update(t *testing.T) { - sut := TierBoundaryListDataType{ - TierBoundaryData: []TierBoundaryDataType{ - { - BoundaryId: util.Ptr(TierBoundaryIdType(0)), - LowerBoundaryValue: NewScaledNumberType(1), - }, - { - BoundaryId: util.Ptr(TierBoundaryIdType(1)), - LowerBoundaryValue: NewScaledNumberType(1), - }, - }, - } - - newData := TierBoundaryListDataType{ - TierBoundaryData: []TierBoundaryDataType{ - { - BoundaryId: util.Ptr(TierBoundaryIdType(1)), - LowerBoundaryValue: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TierBoundaryData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.BoundaryId)) - assert.Equal(t, 1.0, item1.LowerBoundaryValue.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.BoundaryId)) - assert.Equal(t, 10.0, item2.LowerBoundaryValue.GetValue()) -} - -func TestTierBoundaryDescriptionListDataType_Update(t *testing.T) { - sut := TierBoundaryDescriptionListDataType{ - TierBoundaryDescriptionData: []TierBoundaryDescriptionDataType{ - { - BoundaryId: util.Ptr(TierBoundaryIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - BoundaryId: util.Ptr(TierBoundaryIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := TierBoundaryDescriptionListDataType{ - TierBoundaryDescriptionData: []TierBoundaryDescriptionDataType{ - { - BoundaryId: util.Ptr(TierBoundaryIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TierBoundaryDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.BoundaryId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.BoundaryId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestCommodityListDataType_Update(t *testing.T) { - sut := CommodityListDataType{ - CommodityData: []CommodityDataType{ - { - CommodityId: util.Ptr(CommodityIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - CommodityId: util.Ptr(CommodityIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := CommodityListDataType{ - CommodityData: []CommodityDataType{ - { - CommodityId: util.Ptr(CommodityIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.CommodityData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.CommodityId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.CommodityId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestTierListDataType_Update(t *testing.T) { - sut := TierListDataType{ - TierData: []TierDataType{ - { - TierId: util.Ptr(TierIdType(0)), - ActiveIncentiveId: []IncentiveIdType{0}, - }, - { - TierId: util.Ptr(TierIdType(1)), - ActiveIncentiveId: []IncentiveIdType{0}, - }, - }, - } - - newData := TierListDataType{ - TierData: []TierDataType{ - { - TierId: util.Ptr(TierIdType(1)), - ActiveIncentiveId: []IncentiveIdType{1}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TierData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TierId)) - assert.Equal(t, 0, int(item1.ActiveIncentiveId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TierId)) - assert.Equal(t, 1, int(item2.ActiveIncentiveId[0])) -} - -func TestTierIncentiveRelationListDataType_Update(t *testing.T) { - sut := TierIncentiveRelationListDataType{ - TierIncentiveRelationData: []TierIncentiveRelationDataType{ - { - TierId: util.Ptr(TierIdType(0)), - IncentiveId: []IncentiveIdType{0}, - }, - { - TierId: util.Ptr(TierIdType(1)), - IncentiveId: []IncentiveIdType{0}, - }, - }, - } - - newData := TierIncentiveRelationListDataType{ - TierIncentiveRelationData: []TierIncentiveRelationDataType{ - { - TierId: util.Ptr(TierIdType(1)), - IncentiveId: []IncentiveIdType{1}, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TierIncentiveRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TierId)) - assert.Equal(t, 0, int(item1.IncentiveId[0])) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TierId)) - assert.Equal(t, 1, int(item2.IncentiveId[0])) -} - -func TestTierDescriptionListDataType_Update(t *testing.T) { - sut := TierDescriptionListDataType{ - TierDescriptionData: []TierDescriptionDataType{ - { - TierId: util.Ptr(TierIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - TierId: util.Ptr(TierIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := TierDescriptionListDataType{ - TierDescriptionData: []TierDescriptionDataType{ - { - TierId: util.Ptr(TierIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TierDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TierId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TierId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestIncentiveListDataType_Update(t *testing.T) { - sut := IncentiveListDataType{ - IncentiveData: []IncentiveDataType{ - { - IncentiveId: util.Ptr(IncentiveIdType(0)), - Value: NewScaledNumberType(1), - }, - { - IncentiveId: util.Ptr(IncentiveIdType(1)), - Value: NewScaledNumberType(1), - }, - }, - } - - newData := IncentiveListDataType{ - IncentiveData: []IncentiveDataType{ - { - IncentiveId: util.Ptr(IncentiveIdType(1)), - Value: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.IncentiveData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.IncentiveId)) - assert.Equal(t, 1.0, item1.Value.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.IncentiveId)) - assert.Equal(t, 10.0, item2.Value.GetValue()) -} - -func TestIncentiveDescriptionListDataType_Update(t *testing.T) { - sut := IncentiveDescriptionListDataType{ - IncentiveDescriptionData: []IncentiveDescriptionDataType{ - { - IncentiveId: util.Ptr(IncentiveIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - IncentiveId: util.Ptr(IncentiveIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := IncentiveDescriptionListDataType{ - IncentiveDescriptionData: []IncentiveDescriptionDataType{ - { - IncentiveId: util.Ptr(IncentiveIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.IncentiveDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.IncentiveId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.IncentiveId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/taskmanagement.go b/spine/model/taskmanagement.go deleted file mode 100644 index 73886d3f..00000000 --- a/spine/model/taskmanagement.go +++ /dev/null @@ -1,159 +0,0 @@ -package model - -type TaskManagementJobIdType uint - -type TaskManagementJobStateType string - -const ( - // DirectControlActivityStateType - TaskManagementJobStateTypeRunning TaskManagementJobStateType = "Running" - TaskManagementJobStateTypePaused TaskManagementJobStateType = "paused" - TaskManagementJobStateTypeInactive TaskManagementJobStateType = "inactive" - - // HvacOverrunStatusType - TaskManagementJobStateTypeActive TaskManagementJobStateType = "active" - TaskManagementJobStateTypeFinished TaskManagementJobStateType = "finished" - - // LoadControlEventStateType - TaskManagementJobStateTypeEventAccepted TaskManagementJobStateType = "eventAccepted" - TaskManagementJobStateTypeEventStarted TaskManagementJobStateType = "eventStarted" - TaskManagementJobStateTypeEventStopped TaskManagementJobStateType = "eventStopped" - TaskManagementJobStateTypeEventRejected TaskManagementJobStateType = "eventRejected" - TaskManagementJobStateTypeEventCancelled TaskManagementJobStateType = "eventCancelled" - TaskManagementJobStateTypeEventError TaskManagementJobStateType = "eventError" - - // PowerSequenceStateType - TaskManagementJobStateTypeScheduled TaskManagementJobStateType = "scheduled" - TaskManagementJobStateTypeScheduledPaused TaskManagementJobStateType = "scheduledPaused" - TaskManagementJobStateTypePending TaskManagementJobStateType = "pending" - TaskManagementJobStateTypeCompleted TaskManagementJobStateType = "completed" - TaskManagementJobStateTypeInvalid TaskManagementJobStateType = "invalid" -) - -type TaskManagementJobSourceType string - -const ( - TaskManagementJobSourceTypeInternalMechanism TaskManagementJobSourceType = "InternalMechanism" - TaskManagementJobSourceTypeUserInteraction TaskManagementJobSourceType = "UserInteraction" - TaskManagementJobSourceTypeExternalConfiguration TaskManagementJobSourceType = "ExternalConfiguration" -) - -type TaskManagementDirectControlRelatedType struct{} - -type TaskManagementDirectControlRelatedElementsType struct{} - -type TaskManagementHvacRelatedType struct { - OverrunId *HvacOverrunIdType `json:"overrunId,omitempty"` -} - -type TaskManagementHvacRelatedElementsType struct { - OverrunId *ElementTagType `json:"overrunId,omitempty"` -} - -type TaskManagementLoadControlReleatedType struct { - EventId *LoadControlEventIdType `json:"eventId,omitempty"` -} - -type TaskManagementLoadControlReleatedElementsType struct { - EventId *ElementTagType `json:"eventId,omitempty"` -} - -type TaskManagementPowerSequencesRelatedType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type TaskManagementPowerSequencesRelatedElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` -} - -type TaskManagementSmartEnergyManagementPsRelatedType struct { - SequenceId *PowerSequenceIdType `json:"sequenceId,omitempty"` -} - -type TaskManagementSmartEnergyManagementPsRelatedElementsType struct { - SequenceId *ElementTagType `json:"sequenceId,omitempty"` -} - -type TaskManagementJobDataType struct { - JobId *TaskManagementJobIdType `json:"jobId,omitempty" eebus:"key"` - Timestamp *AbsoluteOrRelativeTimeType `json:"timestamp,omitempty"` - JobState *TaskManagementJobStateType `json:"jobState,omitempty"` - ElapsedTime *DurationType `json:"elapsedTime,omitempty"` - RemainingTime *DurationType `json:"remainingTime,omitempty"` -} - -type TaskManagementJobDataElementsType struct { - JobId *ElementTagType `json:"jobId,omitempty"` - Timestamp *ElementTagType `json:"timestamp,omitempty"` - JobState *ElementTagType `json:"jobState,omitempty"` - ElapsedTime *ElementTagType `json:"elapsedTime,omitempty"` - RemainingTime *ElementTagType `json:"remainingTime,omitempty"` -} - -type TaskManagementJobListDataType struct { - TaskManagementJobData []TaskManagementJobDataType `json:"taskManagementJobData,omitempty"` -} - -type TaskManagementJobListDataSelectorsType struct { - JobId *TaskManagementJobIdType `json:"jobId,omitempty"` - JobState *TaskManagementJobStateType `json:"jobState,omitempty"` -} - -type TaskManagementJobRelationDataType struct { - JobId *TaskManagementJobIdType `json:"jobId,omitempty" eebus:"key"` - DirectControlRelated *TaskManagementDirectControlRelatedType `json:"directControlRelated,omitempty"` - HvacRelated *TaskManagementHvacRelatedType `json:"hvacRelated,omitempty"` - LoadControlReleated *TaskManagementLoadControlReleatedType `json:"loadControlReleated,omitempty"` - PowerSequencesRelated *TaskManagementPowerSequencesRelatedType `json:"powerSequencesRelated,omitempty"` - SmartEnergyManagementPsRelated *TaskManagementSmartEnergyManagementPsRelatedType `json:"smartEnergyManagementPsRelated,omitempty"` -} - -type TaskManagementJobRelationDataElementsType struct { - JobId *ElementTagType `json:"jobId,omitempty"` - DirectControlRelated *TaskManagementDirectControlRelatedElementsType `json:"directControlRelated,omitempty"` - HvacRelated *TaskManagementHvacRelatedElementsType `json:"hvacRelated,omitempty"` - LoadControlReleated *TaskManagementLoadControlReleatedElementsType `json:"loadControlReleated,omitempty"` - PowerSequencesRelated *TaskManagementPowerSequencesRelatedElementsType `json:"powerSequencesRelated,omitempty"` - SmartEnergyManagementPsRelated *TaskManagementSmartEnergyManagementPsRelatedElementsType `json:"smartEnergyManagementPsRelated,omitempty"` -} - -type TaskManagementJobRelationListDataType struct { - TaskManagementJobRelationData []TaskManagementJobRelationDataType `json:"taskManagementJobRelationData,omitempty"` -} - -type TaskManagementJobRelationListDataSelectorsType struct { - JobId *TaskManagementJobIdType `json:"jobId,omitempty"` -} - -type TaskManagementJobDescriptionDataType struct { - JobId *TaskManagementJobIdType `json:"jobId,omitempty" eebus:"key"` - JobSource *TaskManagementJobSourceType `json:"jobSource,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type TaskManagementJobDescriptionDataElementsType struct { - JobId *ElementTagType `json:"jobId,omitempty"` - JobSource *ElementTagType `json:"jobSource,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type TaskManagementJobDescriptionListDataType struct { - TaskManagementJobDescriptionData []TaskManagementJobDescriptionDataType `json:"taskManagementJobDescriptionData,omitempty"` -} - -type TaskManagementJobDescriptionListDataSelectorsType struct { - JobId *TaskManagementJobIdType `json:"jobId,omitempty"` - JobSource *TaskManagementJobSourceType `json:"jobSource,omitempty"` -} - -type TaskManagementOverviewDataType struct { - RemoteControllable *bool `json:"remoteControllable,omitempty"` - JobsActive *bool `json:"jobsActive,omitempty"` -} - -type TaskManagementOverviewDataElementsType struct { - RemoteControllable *ElementTagType `json:"remoteControllable,omitempty"` - JobsActive *ElementTagType `json:"jobsActive,omitempty"` -} diff --git a/spine/model/taskmanagement_additions.go b/spine/model/taskmanagement_additions.go deleted file mode 100644 index 966b31cb..00000000 --- a/spine/model/taskmanagement_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// TaskManagementJobListDataType - -var _ Updater = (*TaskManagementJobListDataType)(nil) - -func (r *TaskManagementJobListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TaskManagementJobDataType - if newList != nil { - newData = newList.(*TaskManagementJobListDataType).TaskManagementJobData - } - - r.TaskManagementJobData = UpdateList(r.TaskManagementJobData, newData, filterPartial, filterDelete) -} - -// TaskManagementJobRelationListDataType - -var _ Updater = (*TaskManagementJobRelationListDataType)(nil) - -func (r *TaskManagementJobRelationListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TaskManagementJobRelationDataType - if newList != nil { - newData = newList.(*TaskManagementJobRelationListDataType).TaskManagementJobRelationData - } - - r.TaskManagementJobRelationData = UpdateList(r.TaskManagementJobRelationData, newData, filterPartial, filterDelete) -} - -// TaskManagementJobDescriptionListDataType - -var _ Updater = (*TaskManagementJobDescriptionListDataType)(nil) - -func (r *TaskManagementJobDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TaskManagementJobDescriptionDataType - if newList != nil { - newData = newList.(*TaskManagementJobDescriptionListDataType).TaskManagementJobDescriptionData - } - - r.TaskManagementJobDescriptionData = UpdateList(r.TaskManagementJobDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/taskmanagement_additions_test.go b/spine/model/taskmanagement_additions_test.go deleted file mode 100644 index 25c88818..00000000 --- a/spine/model/taskmanagement_additions_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestTaskManagementJobListDataType_Update(t *testing.T) { - sut := TaskManagementJobListDataType{ - TaskManagementJobData: []TaskManagementJobDataType{ - { - JobId: util.Ptr(TaskManagementJobIdType(0)), - JobState: util.Ptr(TaskManagementJobStateTypeActive), - }, - { - JobId: util.Ptr(TaskManagementJobIdType(1)), - JobState: util.Ptr(TaskManagementJobStateTypeActive), - }, - }, - } - - newData := TaskManagementJobListDataType{ - TaskManagementJobData: []TaskManagementJobDataType{ - { - JobId: util.Ptr(TaskManagementJobIdType(1)), - JobState: util.Ptr(TaskManagementJobStateTypeCompleted), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TaskManagementJobData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.JobId)) - assert.Equal(t, TaskManagementJobStateTypeActive, *item1.JobState) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.JobId)) - assert.Equal(t, TaskManagementJobStateTypeCompleted, *item2.JobState) -} - -func TestTaskManagementJobRelationListDataType_Update(t *testing.T) { - sut := TaskManagementJobRelationListDataType{ - TaskManagementJobRelationData: []TaskManagementJobRelationDataType{ - { - JobId: util.Ptr(TaskManagementJobIdType(0)), - LoadControlReleated: &TaskManagementLoadControlReleatedType{ - EventId: util.Ptr(LoadControlEventIdType(0)), - }, - }, - { - JobId: util.Ptr(TaskManagementJobIdType(1)), - LoadControlReleated: &TaskManagementLoadControlReleatedType{ - EventId: util.Ptr(LoadControlEventIdType(0)), - }, - }, - }, - } - - newData := TaskManagementJobRelationListDataType{ - TaskManagementJobRelationData: []TaskManagementJobRelationDataType{ - { - JobId: util.Ptr(TaskManagementJobIdType(1)), - LoadControlReleated: &TaskManagementLoadControlReleatedType{ - EventId: util.Ptr(LoadControlEventIdType(1)), - }, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TaskManagementJobRelationData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.JobId)) - assert.Equal(t, 0, int(*item1.LoadControlReleated.EventId)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.JobId)) - assert.Equal(t, 1, int(*item2.LoadControlReleated.EventId)) -} - -func TestTaskManagementJobDescriptionListDataType_Update(t *testing.T) { - sut := TaskManagementJobDescriptionListDataType{ - TaskManagementJobDescriptionData: []TaskManagementJobDescriptionDataType{ - { - JobId: util.Ptr(TaskManagementJobIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - JobId: util.Ptr(TaskManagementJobIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := TaskManagementJobDescriptionListDataType{ - TaskManagementJobDescriptionData: []TaskManagementJobDescriptionDataType{ - { - JobId: util.Ptr(TaskManagementJobIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TaskManagementJobDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.JobId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.JobId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/threshold.go b/spine/model/threshold.go deleted file mode 100644 index 574bbf59..00000000 --- a/spine/model/threshold.go +++ /dev/null @@ -1,85 +0,0 @@ -package model - -type ThresholdIdType uint - -type ThresholdTypeType string - -const ( - ThresholdTypeTypeGoodAbove ThresholdTypeType = "goodAbove" - ThresholdTypeTypeBadAbove ThresholdTypeType = "badAbove" - ThresholdTypeTypeGoodBelow ThresholdTypeType = "goodBelow" - ThresholdTypeTypeBadBelow ThresholdTypeType = "badBelow" - ThresholdTypeTypeMinValueThreshold ThresholdTypeType = "minValueThreshold" - ThresholdTypeTypeMaxValueThreshold ThresholdTypeType = "maxValueThreshold" - ThresholdTypeTypeMinValueThresholdExtreme ThresholdTypeType = "minValueThresholdExtreme" - ThresholdTypeTypeMaxValueThresholdExtreme ThresholdTypeType = "maxValueThresholdExtreme" - ThresholdTypeTypeSagThreshold ThresholdTypeType = "sagThreshold" - ThresholdTypeTypeSwellThreshold ThresholdTypeType = "swellThreshold" -) - -type ThresholdDataType struct { - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty" eebus:"key"` - ThresholdValue *ScaledNumberType `json:"thresholdValue,omitempty"` -} - -type ThresholdDataElementsType struct { - ThresholdId *ElementTagType `json:"thresholdId,omitempty"` - ThresholdValue *ScaledNumberElementsType `json:"thresholdValue,omitempty"` -} - -type ThresholdListDataType struct { - ThresholdData []ThresholdDataType `json:"thresholdData,omitempty"` -} - -type ThresholdListDataSelectorsType struct { - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty"` -} - -type ThresholdConstraintsDataType struct { - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty" eebus:"key"` - ThresholdRangeMin *ScaledNumberType `json:"thresholdRangeMin,omitempty"` - ThresholdRangeMax *ScaledNumberType `json:"thresholdRangeMax,omitempty"` - ThresholdStepSize *ScaledNumberType `json:"thresholdStepSize,omitempty"` -} - -type ThresholdConstraintsDataElementsType struct { - ThresholdId *ElementTagType `json:"thresholdId,omitempty"` - ThresholdRangeMin *ScaledNumberElementsType `json:"thresholdRangeMin,omitempty"` - ThresholdRangeMax *ScaledNumberElementsType `json:"thresholdRangeMax,omitempty"` - ThresholdStepSize *ScaledNumberElementsType `json:"thresholdStepSize,omitempty"` -} - -type ThresholdConstraintsListDataType struct { - ThresholdConstraintsData []ThresholdConstraintsDataType `json:"thresholdConstraintsData,omitempty"` -} - -type ThresholdConstraintsListDataSelectorsType struct { - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty"` -} - -type ThresholdDescriptionDataType struct { - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty" eebus:"key"` - ThresholdType *ThresholdTypeType `json:"thresholdType,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type ThresholdDescriptionDataElementsType struct { - ThresholdId *ElementTagType `json:"thresholdId,omitempty"` - ThresholdType *ElementTagType `json:"thresholdType,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type ThresholdDescriptionListDataType struct { - ThresholdDescriptionData []ThresholdDescriptionDataType `json:"thresholdDescriptionData,omitempty"` -} - -type ThresholdDescriptionListDataSelectorsType struct { - ThresholdId *ThresholdIdType `json:"thresholdId,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} diff --git a/spine/model/threshold_additions.go b/spine/model/threshold_additions.go deleted file mode 100644 index 07299222..00000000 --- a/spine/model/threshold_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// ThresholdListDataType - -var _ Updater = (*ThresholdListDataType)(nil) - -func (r *ThresholdListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []ThresholdDataType - if newList != nil { - newData = newList.(*ThresholdListDataType).ThresholdData - } - - r.ThresholdData = UpdateList(r.ThresholdData, newData, filterPartial, filterDelete) -} - -// ThresholdConstraintsListDataType - -var _ Updater = (*ThresholdConstraintsListDataType)(nil) - -func (r *ThresholdConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []ThresholdConstraintsDataType - if newList != nil { - newData = newList.(*ThresholdConstraintsListDataType).ThresholdConstraintsData - } - - r.ThresholdConstraintsData = UpdateList(r.ThresholdConstraintsData, newData, filterPartial, filterDelete) -} - -// ThresholdDescriptionListDataType - -var _ Updater = (*ThresholdDescriptionListDataType)(nil) - -func (r *ThresholdDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []ThresholdDescriptionDataType - if newList != nil { - newData = newList.(*ThresholdDescriptionListDataType).ThresholdDescriptionData - } - - r.ThresholdDescriptionData = UpdateList(r.ThresholdDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/threshold_additions_test.go b/spine/model/threshold_additions_test.go deleted file mode 100644 index 01f92fa6..00000000 --- a/spine/model/threshold_additions_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestThresholdListDataType_Update(t *testing.T) { - sut := ThresholdListDataType{ - ThresholdData: []ThresholdDataType{ - { - ThresholdId: util.Ptr(ThresholdIdType(0)), - ThresholdValue: NewScaledNumberType(1), - }, - { - ThresholdId: util.Ptr(ThresholdIdType(1)), - ThresholdValue: NewScaledNumberType(1), - }, - }, - } - - newData := ThresholdListDataType{ - ThresholdData: []ThresholdDataType{ - { - ThresholdId: util.Ptr(ThresholdIdType(1)), - ThresholdValue: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ThresholdData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ThresholdId)) - assert.Equal(t, 1.0, item1.ThresholdValue.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ThresholdId)) - assert.Equal(t, 10.0, item2.ThresholdValue.GetValue()) -} - -func TestThresholdConstraintsListDataType_Update(t *testing.T) { - sut := ThresholdConstraintsListDataType{ - ThresholdConstraintsData: []ThresholdConstraintsDataType{ - { - ThresholdId: util.Ptr(ThresholdIdType(0)), - ThresholdRangeMin: NewScaledNumberType(1), - }, - { - ThresholdId: util.Ptr(ThresholdIdType(1)), - ThresholdRangeMin: NewScaledNumberType(1), - }, - }, - } - - newData := ThresholdConstraintsListDataType{ - ThresholdConstraintsData: []ThresholdConstraintsDataType{ - { - ThresholdId: util.Ptr(ThresholdIdType(1)), - ThresholdRangeMin: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ThresholdConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ThresholdId)) - assert.Equal(t, 1.0, item1.ThresholdRangeMin.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ThresholdId)) - assert.Equal(t, 10.0, item2.ThresholdRangeMin.GetValue()) -} - -func TestThresholdDescriptionListDataType_Update(t *testing.T) { - sut := ThresholdDescriptionListDataType{ - ThresholdDescriptionData: []ThresholdDescriptionDataType{ - { - ThresholdId: util.Ptr(ThresholdIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - ThresholdId: util.Ptr(ThresholdIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := ThresholdDescriptionListDataType{ - ThresholdDescriptionData: []ThresholdDescriptionDataType{ - { - ThresholdId: util.Ptr(ThresholdIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.ThresholdDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.ThresholdId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.ThresholdId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/timeinformation.go b/spine/model/timeinformation.go deleted file mode 100644 index 1d8e606d..00000000 --- a/spine/model/timeinformation.go +++ /dev/null @@ -1,41 +0,0 @@ -package model - -type TimeInformationDataType struct { - Utc *DateTimeType `json:"utc,omitempty"` - UtcOffset *DurationType `json:"utcOffset,omitempty"` - DayOfWeek *DayOfWeekType `json:"dayOfWeek,omitempty"` - CalendarWeek *CalendarWeekType `json:"calendarWeek,omitempty"` -} - -type TimeInformationDataElementsType struct { - Utc *ElementTagType `json:"utc,omitempty"` - UtcOffset *ElementTagType `json:"utcOffset,omitempty"` - DayOfWeek *ElementTagType `json:"dayOfWeek,omitempty"` - CalendarWeek *ElementTagType `json:"calendarWeek,omitempty"` -} - -type TimeDistributorDataType struct { - IsTimeDistributor *bool `json:"isTimeDistributor,omitempty"` - DistributorPriority *uint `json:"distributorPriority,omitempty"` -} - -type TimeDistributorDataElementsType struct { - IsTimeDistributor *ElementTagType `json:"isTimeDistributor,omitempty"` - DistributorPriority *ElementTagType `json:"distributorPriority,omitempty"` -} - -type TimePrecisionDataType struct { - IsSynchronised *bool `json:"isSynchronised,omitempty"` - LastSyncAt *DateTimeType `json:"lastSyncAt,omitempty"` - ClockDrift *int `json:"clockDrift,omitempty"` -} - -type TimePrecisionDataElementsType struct { - IsSynchronised *ElementTagType `json:"isSynchronised,omitempty"` - LastSyncAt *ElementTagType `json:"lastSyncAt,omitempty"` - ClockDrift *ElementTagType `json:"clockDrift,omitempty"` -} - -type TimeDistributorEnquiryCallType struct{} - -type TimeDistributorEnquiryCallElementsType struct{} diff --git a/spine/model/timeseries.go b/spine/model/timeseries.go deleted file mode 100644 index 91cae18a..00000000 --- a/spine/model/timeseries.go +++ /dev/null @@ -1,133 +0,0 @@ -package model - -type TimeSeriesIdType uint - -type TimeSeriesSlotIdType uint - -type TimeSeriesSlotCountType TimeSeriesSlotIdType - -type TimeSeriesTypeType string - -const ( - TimeSeriesTypeTypePlan TimeSeriesTypeType = "plan" - TimeSeriesTypeTypeSingleDemand TimeSeriesTypeType = "singleDemand" - TimeSeriesTypeTypeConstraints TimeSeriesTypeType = "constraints" - TimeSeriesTypeTypeEnergyRequest TimeSeriesTypeType = "energyRequest" - TimeSeriesTypeTypeDischargingEnergyRequest TimeSeriesTypeType = "dischargingEnergyRequest" - TimeSeriesTypeTypeConsumptionLimitCurve TimeSeriesTypeType = "consumptionLimitCurve" - TimeSeriesTypeTypeProductionLimitCurve TimeSeriesTypeType = "productionLimitCurve" -) - -type TimeSeriesSlotType struct { - TimeSeriesSlotId *TimeSeriesSlotIdType `json:"timeSeriesSlotId,omitempty"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` - Duration *DurationType `json:"duration,omitempty"` - RecurrenceInformation *AbsoluteOrRecurringTimeType `json:"recurrenceInformation,omitempty"` - Value *ScaledNumberType `json:"value,omitempty"` - MinValue *ScaledNumberType `json:"minValue,omitempty"` - MaxValue *ScaledNumberType `json:"maxValue,omitempty"` -} - -type TimeSeriesSlotElementsType struct { - TimeSeriesSlotId *ElementTagType `json:"timeSeriesSlotId,omitempty"` - TimePeriod *ElementTagType `json:"timePeriod,omitempty"` - Duration *ElementTagType `json:"duration,omitempty"` - RecurrenceInformation *AbsoluteOrRecurringTimeElementsType `json:"recurrenceInformation,omitempty"` - Value *ScaledNumberElementsType `json:"value,omitempty"` - MinValue *ScaledNumberElementsType `json:"minValue,omitempty"` - MaxValue *ScaledNumberElementsType `json:"maxValue,omitempty"` -} - -type TimeSeriesDataType struct { - TimeSeriesId *TimeSeriesIdType `json:"timeSeriesId,omitempty" eebus:"key"` - TimePeriod *TimePeriodType `json:"timePeriod,omitempty"` - TimeSeriesSlot []TimeSeriesSlotType `json:"timeSeriesSlot"` -} - -type TimeSeriesDataElementsType struct { - TimeSeriesId *ElementTagType `json:"timeSeriesId,omitempty"` - TimePeriod *TimePeriodElementsType `json:"timePeriod,omitempty"` - TimeSeriesSlot *TimeSeriesSlotElementsType `json:"timeSeriesSlot"` -} - -type TimeSeriesListDataType struct { - TimeSeriesData []TimeSeriesDataType `json:"timeSeriesData,omitempty"` -} - -type TimeSeriesListDataSelectorsType struct { - TimeSeriesId *TimeSeriesIdType `json:"timeSeriesId,omitempty"` - TimeSeriesSlotId *TimeSeriesSlotIdType `json:"timeSeriesSlotId,omitempty"` -} - -type TimeSeriesDescriptionDataType struct { - TimeSeriesId *TimeSeriesIdType `json:"timeSeriesId,omitempty" eebus:"key"` - TimeSeriesType *TimeSeriesTypeType `json:"timeSeriesType,omitempty"` - TimeSeriesWriteable *bool `json:"timeSeriesWriteable,omitempty"` - UpdateRequired *bool `json:"updateRequired,omitempty"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - Currency *CurrencyType `json:"currency,omitempty"` - Unit *UnitOfMeasurementType `json:"unit,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} - -type TimeSeriesDescriptionDataElementsType struct { - TimeSeriesId *ElementTagType `json:"timeSeriesId,omitempty"` - TimeSeriesType *ElementTagType `json:"timeSeriesType,omitempty"` - TimeSeriesWriteable *ElementTagType `json:"timeSeriesWriteable,omitempty"` - UpdateRequired *ElementTagType `json:"updateRequired,omitempty"` - MeasurementId *ElementTagType `json:"measurementId,omitempty"` - Currency *ElementTagType `json:"currency,omitempty"` - Unit *ElementTagType `json:"unit,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` - ScopeType *ElementTagType `json:"scopeType,omitempty"` -} - -type TimeSeriesDescriptionListDataType struct { - TimeSeriesDescriptionData []TimeSeriesDescriptionDataType `json:"timeSeriesDescriptionData,omitempty"` -} - -type TimeSeriesDescriptionListDataSelectorsType struct { - TimeSeriesId *TimeSeriesIdType `json:"timeSeriesId,omitempty"` - TimeSeriesType *TimeSeriesTypeType `json:"timeSeriesType,omitempty"` - MeasurementId *MeasurementIdType `json:"measurementId,omitempty"` - ScopeType *ScopeTypeType `json:"scopeType,omitempty"` -} - -type TimeSeriesConstraintsDataType struct { - TimeSeriesId *TimeSeriesIdType `json:"timeSeriesId,omitempty" eebus:"key"` - SlotCountMin *TimeSeriesSlotCountType `json:"slotCountMin,omitempty"` - SlotCountMax *TimeSeriesSlotCountType `json:"slotCountMax,omitempty"` - SlotDurationMin *DurationType `json:"slotDurationMin,omitempty"` - SlotDurationMax *DurationType `json:"slotDurationMax,omitempty"` - SlotDurationStepSize *DurationType `json:"slotDurationStepSize,omitempty"` - EarliestTimeSeriesStartTime *AbsoluteOrRelativeTimeType `json:"earliestTimeSeriesStartTime,omitempty"` - LatestTimeSeriesEndTime *AbsoluteOrRelativeTimeType `json:"latestTimeSeriesEndTime,omitempty"` - SlotValueMin *ScaledNumberType `json:"slotValueMin,omitempty"` - SlotValueMax *ScaledNumberType `json:"slotValueMax,omitempty"` - SlotValueStepSize *ScaledNumberType `json:"slotValueStepSize,omitempty"` -} - -type TimeSeriesConstraintsDataElementsType struct { - TimeSeriesId *ElementTagType `json:"timeSeriesId,omitempty"` - SlotCountMin *ElementTagType `json:"slotCountMin,omitempty"` - SlotCountMax *ElementTagType `json:"slotCountMax,omitempty"` - SlotDurationMin *ElementTagType `json:"slotDurationMin,omitempty"` - SlotDurationMax *ElementTagType `json:"slotDurationMax,omitempty"` - SlotDurationStepSize *ElementTagType `json:"slotDurationStepSize,omitempty"` - EarliestTimeSeriesStartTime *ElementTagType `json:"earliestTimeSeriesStartTime,omitempty"` - LatestTimeSeriesEndTime *ElementTagType `json:"latestTimeSeriesEndTime,omitempty"` - SlotValueMin *ElementTagType `json:"slotValueMin,omitempty"` - SlotValueMax *ElementTagType `json:"slotValueMax,omitempty"` - SlotValueStepSize *ElementTagType `json:"slotValueStepSize,omitempty"` -} - -type TimeSeriesConstraintsListDataType struct { - TimeSeriesConstraintsData []TimeSeriesConstraintsDataType `json:"timeSeriesConstraintsData,omitempty"` -} - -type TimeSeriesConstraintsListDataSelectorsType struct { - TimeSeriesId *TimeSeriesIdType `json:"timeSeriesId,omitempty"` -} diff --git a/spine/model/timeseries_additions.go b/spine/model/timeseries_additions.go deleted file mode 100644 index 06c1f79e..00000000 --- a/spine/model/timeseries_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// TimeSeriesListDataType - -var _ Updater = (*TimeSeriesListDataType)(nil) - -func (r *TimeSeriesListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TimeSeriesDataType - if newList != nil { - newData = newList.(*TimeSeriesListDataType).TimeSeriesData - } - - r.TimeSeriesData = UpdateList(r.TimeSeriesData, newData, filterPartial, filterDelete) -} - -// TimeSeriesDescriptionListDataType - -var _ Updater = (*TimeSeriesDescriptionListDataType)(nil) - -func (r *TimeSeriesDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TimeSeriesDescriptionDataType - if newList != nil { - newData = newList.(*TimeSeriesDescriptionListDataType).TimeSeriesDescriptionData - } - - r.TimeSeriesDescriptionData = UpdateList(r.TimeSeriesDescriptionData, newData, filterPartial, filterDelete) -} - -// TimeSeriesConstraintsListDataType - -var _ Updater = (*TimeSeriesConstraintsListDataType)(nil) - -func (r *TimeSeriesConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TimeSeriesConstraintsDataType - if newList != nil { - newData = newList.(*TimeSeriesConstraintsListDataType).TimeSeriesConstraintsData - } - - r.TimeSeriesConstraintsData = UpdateList(r.TimeSeriesConstraintsData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/timeseries_additions_test.go b/spine/model/timeseries_additions_test.go deleted file mode 100644 index 654cd409..00000000 --- a/spine/model/timeseries_additions_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestTimeSeriesListDataType_Update(t *testing.T) { - sut := TimeSeriesListDataType{ - TimeSeriesData: []TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(0)), - TimeSeriesSlot: []TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(0)), - }, - }, - }, - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), - TimeSeriesSlot: []TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(0)), - }, - }, - }, - }, - } - - newData := TimeSeriesListDataType{ - TimeSeriesData: []TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), - TimeSeriesSlot: []TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(1)), - }, - }, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TimeSeriesData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TimeSeriesId)) - assert.Equal(t, 0, int(*item1.TimeSeriesSlot[0].TimeSeriesSlotId)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TimeSeriesId)) - assert.Equal(t, 1, int(*item2.TimeSeriesSlot[0].TimeSeriesSlotId)) -} - -func TestTimeSeriesListDataType_Update_02(t *testing.T) { - sut := TimeSeriesListDataType{ - TimeSeriesData: []TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), - TimePeriod: &TimePeriodType{ - StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), - EndTime: util.Ptr(AbsoluteOrRelativeTimeType("P6D")), - }, - TimeSeriesSlot: []TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(0)), - TimePeriod: &TimePeriodType{ - StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), - EndTime: util.Ptr(AbsoluteOrRelativeTimeType("P6D")), - }, - MaxValue: NewScaledNumberType(10000), - }, - }, - }, - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(2)), - TimePeriod: &TimePeriodType{ - StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), - }, - TimeSeriesSlot: []TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(0)), - Duration: util.Ptr(DurationType("P1DT6H46M33S")), - MaxValue: NewScaledNumberType(0), - }, - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(1)), - Duration: util.Ptr(DurationType("PT7H37M53S")), - MaxValue: NewScaledNumberType(4410), - }, - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(2)), - Duration: util.Ptr(DurationType("PT38M")), - MaxValue: NewScaledNumberType(0), - }, - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(3)), - Duration: util.Ptr(DurationType("PT32M")), - MaxValue: NewScaledNumberType(4410), - }, - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(4)), - Duration: util.Ptr(DurationType("P1D")), - MaxValue: NewScaledNumberType(0), - }, - }, - }, - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(3)), - TimePeriod: &TimePeriodType{ - StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), - }, - TimeSeriesSlot: []TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(1)), - Duration: util.Ptr(DurationType("P1DT15H24M57S")), - Value: NewScaledNumberType(44229), - MaxValue: NewScaledNumberType(49629), - }, - }, - }, - }, - } - - newData := TimeSeriesListDataType{ - TimeSeriesData: []TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(3)), - TimePeriod: &TimePeriodType{ - StartTime: util.Ptr(AbsoluteOrRelativeTimeType("PT0S")), - }, - TimeSeriesSlot: []TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(TimeSeriesSlotIdType(1)), - Duration: util.Ptr(DurationType("P1DT15H16M50S")), - Value: NewScaledNumberType(11539), - MaxValue: NewScaledNumberType(49629), - }, - }, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TimeSeriesData - // check the non changing items - assert.Equal(t, 3, len(data)) - item1 := data[0] - assert.Equal(t, 1, int(*item1.TimeSeriesId)) - assert.Equal(t, 0, int(*item1.TimeSeriesSlot[0].TimeSeriesSlotId)) - // check properties of updated item - item2 := data[2] - assert.Equal(t, 3, int(*item2.TimeSeriesId)) - assert.Equal(t, 1, int(*item2.TimeSeriesSlot[0].TimeSeriesSlotId)) - assert.Equal(t, 11539, int(item2.TimeSeriesSlot[0].Value.GetValue())) -} - -func TestTimeSeriesDescriptionListDataType_Update(t *testing.T) { - sut := TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []TimeSeriesDescriptionDataType{ - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []TimeSeriesDescriptionDataType{ - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TimeSeriesDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TimeSeriesId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TimeSeriesId)) - assert.Equal(t, "new", string(*item2.Description)) -} - -func TestTimeSeriesConstraintsListDataType_Update(t *testing.T) { - sut := TimeSeriesConstraintsListDataType{ - TimeSeriesConstraintsData: []TimeSeriesConstraintsDataType{ - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(0)), - SlotValueMin: NewScaledNumberType(1), - }, - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), - SlotValueMin: NewScaledNumberType(1), - }, - }, - } - - newData := TimeSeriesConstraintsListDataType{ - TimeSeriesConstraintsData: []TimeSeriesConstraintsDataType{ - { - TimeSeriesId: util.Ptr(TimeSeriesIdType(1)), - SlotValueMin: NewScaledNumberType(10), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TimeSeriesConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TimeSeriesId)) - assert.Equal(t, 1.0, item1.SlotValueMin.GetValue()) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TimeSeriesId)) - assert.Equal(t, 10.0, item2.SlotValueMin.GetValue()) -} diff --git a/spine/model/timetable.go b/spine/model/timetable.go deleted file mode 100644 index 2a0dbfde..00000000 --- a/spine/model/timetable.go +++ /dev/null @@ -1,96 +0,0 @@ -package model - -type TimeTableIdType uint - -type TimeSlotIdType uint - -type TimeSlotCountType TimeSlotIdType - -type TimeSlotTimeModeType string - -const ( - TimeSlotTimeModeTypeAbsolute TimeSlotTimeModeType = "absolute" - TimeSlotTimeModeTypeRecurring TimeSlotTimeModeType = "recurring" - TimeSlotTimeModeTypeBoth TimeSlotTimeModeType = "both" -) - -type TimeTableDataType struct { - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty" eebus:"key"` - TimeSlotId *TimeSlotIdType `json:"timeSlotId,omitempty"` - RecurrenceInformation *RecurrenceInformationType `json:"recurrenceInformation,omitempty"` - StartTime *AbsoluteOrRecurringTimeType `json:"startTime,omitempty"` - EndTime *AbsoluteOrRecurringTimeType `json:"endTime,omitempty"` -} - -type TimeTableDataElementsType struct { - TimeTableId *ElementTagType `json:"timeTableId,omitempty"` - TimeSlotId *ElementTagType `json:"timeSlotId,omitempty"` - RecurrenceInformation *RecurrenceInformationElementsType `json:"recurrenceInformation,omitempty"` - StartTime *AbsoluteOrRecurringTimeElementsType `json:"startTime,omitempty"` - EndTime *AbsoluteOrRecurringTimeElementsType `json:"endTime,omitempty"` -} - -type TimeTableListDataType struct { - TimeTableData []TimeTableDataType `json:"timeTableData,omitempty"` -} - -type TimeTableListDataSelectorsType struct { - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty"` - TimeSlotId *TimeSlotIdType `json:"timeSlotId,omitempty"` -} - -type TimeTableConstraintsDataType struct { - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty" eebus:"key"` - SlotCountMin *TimeSlotCountType `json:"slotCountMin,omitempty"` - SlotCountMax *TimeSlotCountType `json:"slotCountMax,omitempty"` - SlotDurationMin *DurationType `json:"slotDurationMin,omitempty"` - SlotDurationMax *DurationType `json:"slotDurationMax,omitempty"` - SlotDurationStepSize *DurationType `json:"slotDurationStepSize,omitempty"` - SlotShiftStepSize *DurationType `json:"slotShiftStepSize,omitempty"` - FirstSlotBeginsAt *TimeType `json:"firstSlotBeginsAt,omitempty"` -} - -type TimeTableConstraintsDataElementsType struct { - TimeTableId *ElementTagType `json:"timeTableId,omitempty"` - SlotCountMin *ElementTagType `json:"slotCountMin,omitempty"` - SlotCountMax *ElementTagType `json:"slotCountMax,omitempty"` - SlotDurationMin *ElementTagType `json:"slotDurationMin,omitempty"` - SlotDurationMax *ElementTagType `json:"slotDurationMax,omitempty"` - SlotDurationStepSize *ElementTagType `json:"slotDurationStepSize,omitempty"` - SlotShiftStepSize *ElementTagType `json:"slotShiftStepSize,omitempty"` - FirstSlotBeginsAt *ElementTagType `json:"firstSlotBeginsAt,omitempty"` -} - -type TimeTableConstraintsListDataType struct { - TimeTableConstraintsData []TimeTableConstraintsDataType `json:"timeTableConstraintsData,omitempty"` -} - -type TimeTableConstraintsListDataSelectorsType struct { - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty"` -} - -type TimeTableDescriptionDataType struct { - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty" eebus:"key"` - TimeSlotCountChangeable *bool `json:"timeSlotCountChangeable,omitempty"` - TimeSlotTimesChangeable *bool `json:"timeSlotTimesChangeable,omitempty"` - TimeSlotTimeMode *TimeSlotTimeModeType `json:"timeSlotTimeMode,omitempty"` - Label *LabelType `json:"label,omitempty"` - Description *DescriptionType `json:"description,omitempty"` -} - -type TimeTableDescriptionDataElementsType struct { - TimeTableId *ElementTagType `json:"timeTableId,omitempty"` - TimeSlotCountChangeable *ElementTagType `json:"timeSlotCountChangeable,omitempty"` - TimeSlotTimesChangeable *ElementTagType `json:"timeSlotTimesChangeable,omitempty"` - TimeSlotTimeMode *ElementTagType `json:"timeSlotTimeMode,omitempty"` - Label *ElementTagType `json:"label,omitempty"` - Description *ElementTagType `json:"description,omitempty"` -} - -type TimeTableDescriptionListDataType struct { - TimeTableDescriptionData []TimeTableDescriptionDataType `json:"timeTableDescriptionData,omitempty"` -} - -type TimeTableDescriptionListDataSelectorsType struct { - TimeTableId *TimeTableIdType `json:"timeTableId,omitempty"` -} diff --git a/spine/model/timetable_additions.go b/spine/model/timetable_additions.go deleted file mode 100644 index 2b336b82..00000000 --- a/spine/model/timetable_additions.go +++ /dev/null @@ -1,40 +0,0 @@ -package model - -// TimeTableListDataType - -var _ Updater = (*TimeTableListDataType)(nil) - -func (r *TimeTableListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TimeTableDataType - if newList != nil { - newData = newList.(*TimeTableListDataType).TimeTableData - } - - r.TimeTableData = UpdateList(r.TimeTableData, newData, filterPartial, filterDelete) -} - -// TimeTableConstraintsListDataType - -var _ Updater = (*TimeTableConstraintsListDataType)(nil) - -func (r *TimeTableConstraintsListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TimeTableConstraintsDataType - if newList != nil { - newData = newList.(*TimeTableConstraintsListDataType).TimeTableConstraintsData - } - - r.TimeTableConstraintsData = UpdateList(r.TimeTableConstraintsData, newData, filterPartial, filterDelete) -} - -// TimeTableDescriptionListDataType - -var _ Updater = (*TimeTableDescriptionListDataType)(nil) - -func (r *TimeTableDescriptionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []TimeTableDescriptionDataType - if newList != nil { - newData = newList.(*TimeTableDescriptionListDataType).TimeTableDescriptionData - } - - r.TimeTableDescriptionData = UpdateList(r.TimeTableDescriptionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/timetable_additions_test.go b/spine/model/timetable_additions_test.go deleted file mode 100644 index 0f4c9844..00000000 --- a/spine/model/timetable_additions_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestTimeTableListDataType_Update(t *testing.T) { - sut := TimeTableListDataType{ - TimeTableData: []TimeTableDataType{ - { - TimeTableId: util.Ptr(TimeTableIdType(0)), - RecurrenceInformation: &RecurrenceInformationType{ - ExecutionCount: util.Ptr(uint(1)), - }, - }, - { - TimeTableId: util.Ptr(TimeTableIdType(1)), - RecurrenceInformation: &RecurrenceInformationType{ - ExecutionCount: util.Ptr(uint(1)), - }, - }, - }, - } - - newData := TimeTableListDataType{ - TimeTableData: []TimeTableDataType{ - { - TimeTableId: util.Ptr(TimeTableIdType(1)), - RecurrenceInformation: &RecurrenceInformationType{ - ExecutionCount: util.Ptr(uint(10)), - }, - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TimeTableData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TimeTableId)) - assert.Equal(t, 1, int(*item1.RecurrenceInformation.ExecutionCount)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TimeTableId)) - assert.Equal(t, 10, int(*item2.RecurrenceInformation.ExecutionCount)) -} - -func TestTimeTableConstraintsListDataType_Update(t *testing.T) { - sut := TimeTableConstraintsListDataType{ - TimeTableConstraintsData: []TimeTableConstraintsDataType{ - { - TimeTableId: util.Ptr(TimeTableIdType(0)), - SlotCountMin: util.Ptr(TimeSlotCountType(1)), - }, - { - TimeTableId: util.Ptr(TimeTableIdType(1)), - SlotCountMin: util.Ptr(TimeSlotCountType(1)), - }, - }, - } - - newData := TimeTableConstraintsListDataType{ - TimeTableConstraintsData: []TimeTableConstraintsDataType{ - { - TimeTableId: util.Ptr(TimeTableIdType(1)), - SlotCountMin: util.Ptr(TimeSlotCountType(10)), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TimeTableConstraintsData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TimeTableId)) - assert.Equal(t, 1, int(*item1.SlotCountMin)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TimeTableId)) - assert.Equal(t, 10, int(*item2.SlotCountMin)) -} - -func TestTimeTableDescriptionListDataType_Update(t *testing.T) { - sut := TimeTableDescriptionListDataType{ - TimeTableDescriptionData: []TimeTableDescriptionDataType{ - { - TimeTableId: util.Ptr(TimeTableIdType(0)), - Description: util.Ptr(DescriptionType("old")), - }, - { - TimeTableId: util.Ptr(TimeTableIdType(1)), - Description: util.Ptr(DescriptionType("old")), - }, - }, - } - - newData := TimeTableDescriptionListDataType{ - TimeTableDescriptionData: []TimeTableDescriptionDataType{ - { - TimeTableId: util.Ptr(TimeTableIdType(1)), - Description: util.Ptr(DescriptionType("new")), - }, - }, - } - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data := sut.TimeTableDescriptionData - // check the non changing items - assert.Equal(t, 2, len(data)) - item1 := data[0] - assert.Equal(t, 0, int(*item1.TimeTableId)) - assert.Equal(t, "old", string(*item1.Description)) - // check properties of updated item - item2 := data[1] - assert.Equal(t, 1, int(*item2.TimeTableId)) - assert.Equal(t, "new", string(*item2.Description)) -} diff --git a/spine/model/update.go b/spine/model/update.go deleted file mode 100644 index 8feec337..00000000 --- a/spine/model/update.go +++ /dev/null @@ -1,301 +0,0 @@ -package model - -import ( - "reflect" - "sort" - - "github.com/enbility/eebus-go/util" -) - -type Updater interface { - UpdateList(newList any, filterPartial, filterDelete *FilterType) -} - -// Generates a new list of function items by applying the rules mentioned in the spec -// (EEBus_SPINE_TS_ProtocolSpecification.pdf; chapter "5.3.4 Restricted function exchange with cmdOptions"). -// The given data provider is used the get the current items and the items and the filters in the payload. -func UpdateList[T any](existingData []T, newData []T, filterPartial, filterDelete *FilterType) []T { - // process delete filter (with selectors and elements) - if filterDelete != nil { - if filterData, err := filterDelete.Data(); err == nil { - existingData = deleteFilteredData(existingData, filterData) - } - } - - // process update filter (with selectors and elements) - if filterPartial != nil { - if filterData, err := filterPartial.Data(); err == nil { - return copyToSelectedData(existingData, filterData, &newData[0]) - } - } - - // check if items have no identifiers - // Currently all fields marked as key are required - // TODO: check how to handle if only one identifier is provided - if len(newData) > 0 && !HasIdentifiers(newData[0]) { - // no identifiers specified --> copy data to all existing items - // (see EEBus_SPINE_TS_ProtocolSpecification.pdf, Table 7: Considered cmdOptions combinations for classifier "notify") - return copyToAllData(existingData, &newData[0]) - } - - result := Merge(existingData, newData) - - result = SortData(result) - - return result -} - -// return a list of field names that have the eebus tag "key" -func keyFieldNames(item any) []string { - var result []string - - v := reflect.ValueOf(item) - t := reflect.TypeOf(item) - - if v.Kind() != reflect.Struct { - return result - } - - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - if f.Kind() != reflect.Ptr { - continue - } - - sf := v.Type().Field(i) - eebusTags := EEBusTags(sf) - _, exists := eebusTags[EEBusTagKey] - if !exists { - continue - } - - fieldName := t.Field(i).Name - result = append(result, fieldName) - } - - return result -} - -func HasIdentifiers(data any) bool { - keys := keyFieldNames(data) - - v := reflect.ValueOf(data) - - for _, fieldName := range keys { - f := v.FieldByName(fieldName) - - if f.IsNil() || !f.IsValid() { - return false - } - } - - return true -} - -// sort slices by fields that have eebus tag "key" -func SortData[T any](data []T) []T { - if len(data) == 0 { - return data - } - - keys := keyFieldNames(data[0]) - - if len(keys) == 0 { - return data - } - - sort.Slice(data, func(i, j int) bool { - item1 := data[i] - item2 := data[j] - - item1V := reflect.ValueOf(item1) - item2V := reflect.ValueOf(item2) - - // if the fields don't match, don't do anything - if item1V.NumField() != item2V.NumField() { - return false - } - - for _, fieldName := range keys { - f1 := item1V.FieldByName(fieldName) - f2 := item2V.FieldByName(fieldName) - if f1.Type().Kind() != reflect.Ptr || f2.Type().Kind() != reflect.Ptr { - return false - } - - if f1.IsNil() || f2.IsNil() || !f1.IsValid() || !f2.IsValid() { - return false - } - - if f1.Elem().Kind() != reflect.Uint || f2.Elem().Kind() != reflect.Uint { - return false - } - - value1 := f1.Elem().Uint() - value2 := f2.Elem().Uint() - - if value1 != value2 { - return value1 < value2 - } - } - - return false - }) - - return data -} - -func copyToSelectedData[T any](existingData []T, filterData *FilterData, newData *T) []T { - if filterData.Selector == nil { - return existingData - } - - for i := range existingData { - if filterData.SelectorMatch(util.Ptr(existingData[i])) { - CopyNonNilDataFromItemToItem(newData, &existingData[i]) - break - } - } - return existingData -} - -func copyToAllData[T any](existingData []T, newData *T) []T { - for i := range existingData { - CopyNonNilDataFromItemToItem(newData, &existingData[i]) - } - return existingData -} - -func deleteFilteredData[T any](existingData []T, filterData *FilterData) []T { - if filterData.Elements == nil && filterData.Selector == nil { - return existingData - } - - result := []T{} - for i := range existingData { - if filterData.Selector != nil && filterData.Elements != nil { - // selector and elements filter - - // remove the fields defined in element if the item matches - if filterData.SelectorMatch(util.Ptr(existingData[i])) { - RemoveElementFromItem(&existingData[i], filterData.Elements) - result = append(result, existingData[i]) - } else { - result = append(result, existingData[i]) - } - } else if filterData.Selector != nil { - // only selector filter - - // remove the whole item if the item matches - if !filterData.SelectorMatch(util.Ptr(existingData[i])) { - result = append(result, existingData[i]) - } - } else { - // only elements filter - - // remove the fields defined in element - RemoveElementFromItem(&existingData[i], filterData.Elements) - result = append(result, existingData[i]) - } - } - return result -} - -func isFieldValueNil(field interface{}) bool { - if field == nil { - return true - } - - switch reflect.TypeOf(field).Kind() { - case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: - return reflect.ValueOf(field).IsNil() - } - return false -} - -func nonNilElementNames(element any) []string { - var result []string - - v := reflect.ValueOf(element).Elem() - t := reflect.TypeOf(element).Elem() - for i := 0; i < v.NumField(); i++ { - isNil := isFieldValueNil(v.Field(i).Interface()) - if !isNil { - name := t.Field(i).Name - result = append(result, name) - } - } - - return result -} - -func isStringValueInSlice(value string, list []string) bool { - for _, item := range list { - if item == value { - return true - } - } - return false -} - -func RemoveElementFromItem[T any, E any](item *T, element E) { - fieldNamesToBeRemoved := nonNilElementNames(element) - - eV := reflect.ValueOf(element).Elem() - eT := reflect.TypeOf(element).Elem() - iV := reflect.ValueOf(item).Elem() - - // if the fields don't match, don't do anything - if eV.NumField() != iV.NumField() { - return - } - - for i := 0; i < eV.NumField(); i++ { - fieldName := eT.Field(i).Name - if isStringValueInSlice(fieldName, fieldNamesToBeRemoved) { - f := iV.FieldByName(fieldName) - if !f.IsValid() { - continue - } - if !f.CanSet() { - continue - } - - f.Set(reflect.Zero(f.Type())) - } - } -} - -func CopyNonNilDataFromItemToItem[T any](source *T, destination *T) { - if source == nil || destination == nil { - return - } - - sV := reflect.ValueOf(source).Elem() - sT := reflect.TypeOf(source).Elem() - dV := reflect.ValueOf(destination).Elem() - - // if the fields don't match, don't do anything - if sV.NumField() != dV.NumField() { - return - } - - for i := 0; i < sV.NumField(); i++ { - value := sV.Field(i) - if value.IsNil() { - continue - } - - fieldName := sT.Field(i).Name - f := dV.FieldByName(fieldName) - - if !f.IsValid() { - continue - } - if !f.CanSet() { - continue - } - - f.Set(value) - } -} diff --git a/spine/model/update_test.go b/spine/model/update_test.go deleted file mode 100644 index 166adfcf..00000000 --- a/spine/model/update_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -type TestUpdateData struct { - Id *uint `eebus:"key"` - DataItem *int -} - -type TestUpdater struct { - // updateSelectorHashKey *string - // deleteSelectorHashKey *string -} - -func TestUpdateList_NewItem(t *testing.T) { - existingData := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(1))}} - newData := []TestUpdateData{{Id: util.Ptr(uint(2)), DataItem: util.Ptr(int(2))}} - - expectedResult := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(1))}, {Id: util.Ptr(uint(2)), DataItem: util.Ptr(int(2))}} - - // Act - result := UpdateList(existingData, newData, nil, nil) - - assert.Equal(t, expectedResult, result) -} - -func TestUpdateList_ChangedItem(t *testing.T) { - existingData := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(1))}} - newData := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(2))}} - - expectedResult := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(2))}} - - // Act - result := UpdateList(existingData, newData, nil, nil) - - assert.Equal(t, expectedResult, result) -} - -func TestUpdateList_NewAndChangedItem(t *testing.T) { - existingData := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(1))}} - newData := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(2))}, {Id: util.Ptr(uint(3)), DataItem: util.Ptr(int(3))}} - - expectedResult := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(2))}, {Id: util.Ptr(uint(3)), DataItem: util.Ptr(int(3))}} - - // Act - result := UpdateList(existingData, newData, nil, nil) - - assert.Equal(t, expectedResult, result) -} - -func TestUpdateList_ItemWithNoIdentifier(t *testing.T) { - existingData := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(1))}, {Id: util.Ptr(uint(2)), DataItem: util.Ptr(int(2))}} - newData := []TestUpdateData{{DataItem: util.Ptr(int(3))}} - - expectedResult := []TestUpdateData{{Id: util.Ptr(uint(1)), DataItem: util.Ptr(int(3))}, {Id: util.Ptr(uint(2)), DataItem: util.Ptr(int(3))}} - - // Act - result := UpdateList(existingData, newData, nil, nil) - - assert.Equal(t, expectedResult, result) -} - -func TestRemoveFieldFromType(t *testing.T) { - items := &LoadControlLimitListDataType{ - LoadControlLimitData: []LoadControlLimitDataType{ - { - LimitId: util.Ptr(LoadControlLimitIdType(1)), - Value: NewScaledNumberType(16.0), - }, - }, - } - - elements := &LoadControlLimitDataElementsType{ - Value: &ScaledNumberElementsType{}, - } - - RemoveElementFromItem(&items.LoadControlLimitData[0], elements) - - var nilValue *ScaledNumberType - - assert.Equal(t, nilValue, items.LoadControlLimitData[0].Value) -} - -// TODO: Fix, as these tests won't work right now as TestUpdater doesn't use FilterProvider and its data structure -/* -func TestUpdateList_UpdateSelector(t *testing.T) { - existingData := []TestUpdateData{{Id: util.Ptr(1), DataItem: 1}, {Id: util.Ptr(2), DataItem: 2}} - newData := []TestUpdateData{{DataItem: 3}} - - dataProvider := &TestUpdater{ - updateSelectorHashKey: util.Ptr("1"), - } - expectedResult := []TestUpdateData{{Id: util.Ptr(1), DataItem: 3}, {Id: util.Ptr(2), DataItem: 2}} - - // Act - result := UpdateList[TestUpdateData](existingData, newData, dataProvider) - - assert.Equal(t, expectedResult, result) -} - -func TestUpdateList_DeleteSelector(t *testing.T) { - existingData := []TestUpdateData{{Id: util.Ptr(1), DataItem: 1}, {Id: util.Ptr(2), DataItem: 2}} - newData := []TestUpdateData{{Id: util.Ptr(0), DataItem: 0}} - - dataProvider := &TestUpdater{ - deleteSelectorHashKey: util.Ptr("1"), - } - expectedResult := []TestUpdateData{{Id: util.Ptr(2), DataItem: 2}} - - // Act - result := UpdateList[TestUpdateData](existingData, newData, dataProvider) - - assert.Equal(t, expectedResult, result) -} -*/ diff --git a/spine/model/usecaseinformation.go b/spine/model/usecaseinformation.go deleted file mode 100644 index 4fcb86fc..00000000 --- a/spine/model/usecaseinformation.go +++ /dev/null @@ -1,118 +0,0 @@ -package model - -type UseCaseActorType string - -const ( - UseCaseActorTypeBattery UseCaseActorType = "Battery" - UseCaseActorTypeBatterySystem UseCaseActorType = "BatterySystem" - UseCaseActorTypeCEM UseCaseActorType = "CEM" - UseCaseActorTypeConfigurationAppliance UseCaseActorType = "ConfigurationAppliance" - UseCaseActorTypeCompressor UseCaseActorType = "Compressor" - UseCaseActorTypeControllableSystem UseCaseActorType = "ControllableSystem" - UseCaseActorTypeDHWCircuit UseCaseActorType = "DHWCircuit" - UseCaseActorTypeEnergyBroker UseCaseActorType = "EnergyBroker" - UseCaseActorTypeEnergyConsumer UseCaseActorType = "EnergyConsumer" - UseCaseActorTypeEnergyGuard UseCaseActorType = "EnergyGuard" - UseCaseActorTypeEVSE UseCaseActorType = "EVSE" - UseCaseActorTypeEV UseCaseActorType = "EV" - UseCaseActorTypeGridConnectionPoint UseCaseActorType = "GridConnectionPoint" - UseCaseActorTypeHeatPump UseCaseActorType = "HeatPump" - UseCaseActorTypeHeatingCircuit UseCaseActorType = "HeatingCircuit" - UseCaseActorTypeHeatingZone UseCaseActorType = "HeatingZone" - UseCaseActorTypeHVACRoom UseCaseActorType = "HVACRoom" - UseCaseActorTypeInverter UseCaseActorType = "Inverter" - UseCaseActorTypeMonitoredUnit UseCaseActorType = "MonitoredUnit" - UseCaseActorTypeMonitoringAppliance UseCaseActorType = "MonitoringAppliance" - UseCaseActorTypeOutdoorTemperatureSensor UseCaseActorType = "OutdoorTemperatureSensor" - UseCaseActorTypePVString UseCaseActorType = "PVString" - UseCaseActorTypePVSystem UseCaseActorType = "PVSystem" - UseCaseActorTypeSmartAppliance UseCaseActorType = "SmartAppliance" - UseCaseActorTypeVisualizationAppliance UseCaseActorType = "VisualizationAppliance" -) - -type UseCaseNameType string - -const ( - UseCaseNameTypeConfigurationOfDhwSystemFunction UseCaseNameType = "configurationOfDhwSystemFunction" - UseCaseNameTypeConfigurationOfDhwTemperature UseCaseNameType = "configurationOfDhwTemperature" - UseCaseNameTypeConfigurationOfRoomCoolingSystemFunction UseCaseNameType = "configurationOfRoomCoolingSystemFunction" - UseCaseNameTypeConfigurationOfRoomCoolingTemperature UseCaseNameType = "configurationOfRoomCoolingTemperature" - UseCaseNameTypeConfigurationOfRoomHeatingSystemFunction UseCaseNameType = "configurationOfRoomHeatingSystemFunction" - UseCaseNameTypeConfigurationOfRoomHeatingTemperature UseCaseNameType = "configurationOfRoomHeatingTemperature" - UseCaseNameTypeControlOfBattery UseCaseNameType = "controlOfBattery" - UseCaseNameTypeCoordinatedEVCharging UseCaseNameType = "coordinatedEvCharging" - UseCaseNameTypeEVChargingSummary UseCaseNameType = "evChargingSummary" - UseCaseNameTypeEVCommissioningAndConfiguration UseCaseNameType = "evCommissioningAndConfiguration" - UseCaseNameTypeEVSECommissioningAndConfiguration UseCaseNameType = "evseCommissioningAndConfiguration" - UseCaseNameTypeEVStateOfCharge UseCaseNameType = "evStateOfCharge" - UseCaseNameTypeFlexibleLoad UseCaseNameType = "flexibleLoad" - UseCaseNameTypeFlexibleStartForWhiteGoods UseCaseNameType = "flexibleStartForWhiteGoods" - UseCaseNameTypeLimitationOfPowerConsumption UseCaseNameType = "limitationOfPowerConsumption" - UseCaseNameTypeLimitationOfPowerProduction UseCaseNameType = "limitationOfPowerProduction" - UseCaseNameTypeIncentiveTableBasedPowerConsumptionManagement UseCaseNameType = "incentiveTableBasedPowerConsumptionManagement" - UseCaseNameTypeMeasurementOfElectricityDuringEVCharging UseCaseNameType = "measurementOfElectricityDuringEvCharging" - UseCaseNameTypeMonitoringAndControlOfSmartGridReadyConditions UseCaseNameType = "monitoringAndControlOfSmartGridReadyConditions" - UseCaseNameTypeMonitoringOfBattery UseCaseNameType = "monitoringOfBattery" - UseCaseNameTypeMonitoringOfDhwSystemFunction UseCaseNameType = "monitoringOfDhwSystemFunction" - UseCaseNameTypeMonitoringOfDhwTemperature UseCaseNameType = "monitoringOfDhwTemperature" - UseCaseNameTypeMonitoringOfGridConnectionPoint UseCaseNameType = "monitoringOfGridConnectionPoint" - UseCaseNameTypeMonitoringOfInverter UseCaseNameType = "monitoringOfInverter" - UseCaseNameTypeMonitoringOfOutdoorTemperature UseCaseNameType = "monitoringOfOutdoorTemperature" - UseCaseNameTypeMonitoringOfPowerConsumption UseCaseNameType = "monitoringOfPowerConsumption" - UseCaseNameTypeMonitoringOfPvString UseCaseNameType = "monitoringOfPvString" - UseCaseNameTypeMonitoringOfRoomCoolingSystemFunction UseCaseNameType = "monitoringOfRoomCoolingSystemFunction" - UseCaseNameTypeMonitoringOfRoomHeatingSystemFunction UseCaseNameType = "monitoringOfRoomHeatingSystemFunction" - UseCaseNameTypeMonitoringOfRoomTemperature UseCaseNameType = "monitoringOfRoomTemperature" - UseCaseNameTypeOptimizationOfSelfConsumptionByHeatPumpCompressorFlexibility UseCaseNameType = "optimizationOfSelfConsumptionByHeatPumpCompressorFlexibility" - UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging UseCaseNameType = "optimizationOfSelfConsumptionDuringEvCharging" - UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment UseCaseNameType = "overloadProtectionByEvChargingCurrentCurtailment" - UseCaseNameTypeVisualizationOfAggregatedBatteryData UseCaseNameType = "visualizationOfAggregatedBatteryData" - UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData UseCaseNameType = "visualizationOfAggregatedPhotovoltaicData" - UseCaseNameTypeVisualizationOfHeatingAreaName UseCaseNameType = "visualizationOfHeatingAreaName" -) - -type UseCaseScenarioSupportType uint - -type UseCaseSupportType struct { - UseCaseName *UseCaseNameType `json:"useCaseName,omitempty"` - UseCaseVersion *SpecificationVersionType `json:"useCaseVersion,omitempty"` - UseCaseAvailable *bool `json:"useCaseAvailable,omitempty"` - ScenarioSupport []UseCaseScenarioSupportType `json:"scenarioSupport,omitempty"` - UseCaseDocumentSubRevision *string `json:"useCaseDocumentSubRevision,omitempty"` -} - -type UseCaseSupportElementsType struct { - UseCaseName *ElementTagType `json:"useCaseName,omitempty"` - UseCaseVersion *ElementTagType `json:"useCaseVersion,omitempty"` - UseCaseAvailable *ElementTagType `json:"useCaseAvailable,omitempty"` - ScenarioSupport *ElementTagType `json:"scenarioSupport,omitempty"` - UseCaseDocumentSubRevision *ElementTagType `json:"useCaseDocumentSubRevision,omitempty"` -} - -type UseCaseSupportSelectorsType struct { - UseCaseName *UseCaseNameType `json:"useCaseName,omitempty"` - UseCaseVersion *SpecificationVersionType `json:"useCaseVersion,omitempty"` - ScenarioSupport *UseCaseScenarioSupportType `json:"scenarioSupport,omitempty"` -} - -type UseCaseInformationDataType struct { - Address *FeatureAddressType `json:"address,omitempty"` - Actor *UseCaseActorType `json:"actor,omitempty"` - UseCaseSupport []UseCaseSupportType `json:"useCaseSupport,omitempty"` -} - -type UseCaseInformationDataElementsType struct { - Address *ElementTagType `json:"address,omitempty"` - Actor *ElementTagType `json:"actor,omitempty"` - UseCaseSupport *ElementTagType `json:"useCaseSupport,omitempty"` -} - -type UseCaseInformationListDataType struct { - UseCaseInformationData []UseCaseInformationDataType `json:"useCaseInformationData,omitempty"` -} - -type UseCaseInformationListDataSelectorsType struct { - Address *FeatureAddressType `json:"address,omitempty"` - Actor *UseCaseActorType `json:"actor,omitempty"` - UseCaseSupport *UseCaseSupportSelectorsType `json:"useCaseSupport,omitempty"` -} diff --git a/spine/model/usecaseinformation_additions.go b/spine/model/usecaseinformation_additions.go deleted file mode 100644 index d30bc5af..00000000 --- a/spine/model/usecaseinformation_additions.go +++ /dev/null @@ -1,53 +0,0 @@ -package model - -import "sync" - -var uciMux sync.Mutex - -// UseCaseInformationDataType - -// find the matching UseCaseSupport index for a UseCaseNameType -func (u *UseCaseInformationDataType) useCaseSupportIndex(useCaseName UseCaseNameType) (int, bool) { - // get the element with the same entity - for index, item := range u.UseCaseSupport { - if item.UseCaseName != nil && *item.UseCaseName == useCaseName { - return index, true - } - } - - return -1, false -} - -// add a new UseCaseSupportType -func (u *UseCaseInformationDataType) Add(useCase UseCaseSupportType) { - uciMux.Lock() - defer uciMux.Unlock() - - if useCase.UseCaseName == nil { - return - } - - // only add it if it does not exist yet - if _, ok := u.useCaseSupportIndex(*useCase.UseCaseName); ok { - return - } - - u.UseCaseSupport = append(u.UseCaseSupport, useCase) -} - -// remove a UseCaseSupportType with a given UseCaseNameType -func (u *UseCaseInformationDataType) Remove(useCaseName UseCaseNameType) { - uciMux.Lock() - defer uciMux.Unlock() - - var usecases []UseCaseSupportType - - for _, item := range u.UseCaseSupport { - if item.UseCaseName != nil && *item.UseCaseName != useCaseName { - usecases = append(usecases, item) - } - - } - - u.UseCaseSupport = usecases -} diff --git a/spine/model/usecaseinformation_additions_test.go b/spine/model/usecaseinformation_additions_test.go deleted file mode 100644 index b91af46b..00000000 --- a/spine/model/usecaseinformation_additions_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package model - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestUseCaseInformationDataTypeSuite(t *testing.T) { - suite.Run(t, new(UseCaseInformationDataTypeSuite)) -} - -type UseCaseInformationDataTypeSuite struct { - suite.Suite -} - -func (s *UseCaseInformationDataTypeSuite) SetupSuite() {} -func (s *UseCaseInformationDataTypeSuite) TearDownTest() {} - -func (s *UseCaseInformationDataTypeSuite) BeforeTest(suiteName, testName string) {} - -func (s *UseCaseInformationDataTypeSuite) Test_AdditionsAndRemovals() { - ucs := &UseCaseInformationDataType{} - assert.NotNil(s.T(), ucs) - assert.Equal(s.T(), 0, len(ucs.UseCaseSupport)) - - uc := UseCaseSupportType{} - ucs.Add(uc) - assert.Equal(s.T(), 0, len(ucs.UseCaseSupport)) - - uc = UseCaseSupportType{ - UseCaseName: util.Ptr(UseCaseNameTypeControlOfBattery), - } - ucs.Add(uc) - assert.Equal(s.T(), 1, len(ucs.UseCaseSupport)) - - ucs.Add(uc) - assert.Equal(s.T(), 1, len(ucs.UseCaseSupport)) - - ucs.Remove(UseCaseNameTypeCoordinatedEVCharging) - assert.Equal(s.T(), 1, len(ucs.UseCaseSupport)) - - ucs.Remove(UseCaseNameTypeControlOfBattery) - assert.Equal(s.T(), 0, len(ucs.UseCaseSupport)) -} diff --git a/spine/model/version.go b/spine/model/version.go deleted file mode 100644 index f5920776..00000000 --- a/spine/model/version.go +++ /dev/null @@ -1,11 +0,0 @@ -package model - -type SpecificationVersionDataType SpecificationVersionType - -type SpecificationVersionDataElementsType struct{} - -type SpecificationVersionListDataType struct { - SpecificationVersionData []SpecificationVersionDataType `json:"specificationVersionData,omitempty"` -} - -type SpecificationVersionListDataSelectorsType struct{} diff --git a/spine/model/version_additions.go b/spine/model/version_additions.go deleted file mode 100644 index 9c96c823..00000000 --- a/spine/model/version_additions.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -// SpecificationVersionListDataType - -var _ Updater = (*SpecificationVersionListDataType)(nil) - -func (r *SpecificationVersionListDataType) UpdateList(newList any, filterPartial, filterDelete *FilterType) { - var newData []SpecificationVersionDataType - if newList != nil { - newData = newList.(*SpecificationVersionListDataType).SpecificationVersionData - } - - r.SpecificationVersionData = UpdateList(r.SpecificationVersionData, newData, filterPartial, filterDelete) -} diff --git a/spine/model/version_additions_test.go b/spine/model/version_additions_test.go deleted file mode 100644 index 7cf49df7..00000000 --- a/spine/model/version_additions_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package model - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestVersionSuite(t *testing.T) { - suite.Run(t, new(VersionSuite)) -} - -type VersionSuite struct { - suite.Suite -} - -func (s *VersionSuite) SetupSuite() {} -func (s *VersionSuite) TearDownTest() {} - -func (s *VersionSuite) BeforeTest(suiteName, testName string) {} - -func (s *VersionSuite) Test_UpdateList() { - sut := SpecificationVersionListDataType{ - SpecificationVersionData: []SpecificationVersionDataType{ - SpecificationVersionDataType("1.0.0"), - }, - } - - newData := SpecificationVersionListDataType{ - SpecificationVersionData: []SpecificationVersionDataType{ - SpecificationVersionDataType("1.0.1"), - }, - } - - data := sut.SpecificationVersionData - // check properties of updated item - item1 := data[0] - assert.Equal(s.T(), "1.0.0", string(item1)) - - // Act - sut.UpdateList(&newData, NewFilterTypePartial(), nil) - - data = sut.SpecificationVersionData - // check properties of updated item - item1 = data[0] - assert.Equal(s.T(), "1.0.1", string(item1)) -} diff --git a/spine/nodemanagement.go b/spine/nodemanagement.go deleted file mode 100644 index 14480925..00000000 --- a/spine/nodemanagement.go +++ /dev/null @@ -1,111 +0,0 @@ -package spine - -import ( - "fmt" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -const NodeManagementFeatureId uint = 0 - -func NodeManagementAddress(deviceAdress *model.AddressDeviceType) *model.FeatureAddressType { - return &model.FeatureAddressType{ - Entity: []model.AddressEntityType{0}, - Feature: util.Ptr(model.AddressFeatureType(NodeManagementFeatureId)), - Device: deviceAdress, - } -} - -type NodeManagementImpl struct { - *FeatureLocalImpl - entity EntityLocal -} - -func NewNodeManagementImpl(id uint, entity EntityLocal) *NodeManagementImpl { - f := &NodeManagementImpl{ - FeatureLocalImpl: NewFeatureLocalImpl( - id, entity, - model.FeatureTypeTypeNodeManagement, - model.RoleTypeSpecial), - entity: entity, - } - - f.AddFunctionType(model.FunctionTypeNodeManagementDetailedDiscoveryData, true, false) - f.AddFunctionType(model.FunctionTypeNodeManagementUseCaseData, true, false) - f.AddFunctionType(model.FunctionTypeNodeManagementSubscriptionData, true, false) - f.AddFunctionType(model.FunctionTypeNodeManagementSubscriptionRequestCall, false, false) - f.AddFunctionType(model.FunctionTypeNodeManagementSubscriptionDeleteCall, false, false) - f.AddFunctionType(model.FunctionTypeNodeManagementBindingData, true, false) - f.AddFunctionType(model.FunctionTypeNodeManagementBindingRequestCall, false, false) - f.AddFunctionType(model.FunctionTypeNodeManagementBindingDeleteCall, false, false) - if f.Device().FeatureSet() != nil && *f.Device().FeatureSet() != model.NetworkManagementFeatureSetTypeSimple { - f.AddFunctionType(model.FunctionTypeNodeManagementDestinationListData, true, false) - } - - return f -} - -func (r *NodeManagementImpl) Device() DeviceLocal { - return r.entity.Device() -} - -func (r *NodeManagementImpl) HandleMessage(message *Message) *model.ErrorType { - switch { - case message.Cmd.ResultData != nil: - if err := r.processResult(message); err != nil { - _ = r.pendingRequests.Remove(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference) - return err - } - - case message.Cmd.NodeManagementDetailedDiscoveryData != nil: - if err := r.handleMsgDetailedDiscoveryData(message, message.Cmd.NodeManagementDetailedDiscoveryData); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - case message.Cmd.NodeManagementSubscriptionRequestCall != nil: - if err := r.handleMsgSubscriptionRequestCall(message, message.Cmd.NodeManagementSubscriptionRequestCall); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - case message.Cmd.NodeManagementSubscriptionDeleteCall != nil: - if err := r.handleMsgSubscriptionDeleteCall(message, message.Cmd.NodeManagementSubscriptionDeleteCall); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - case message.Cmd.NodeManagementSubscriptionData != nil: - if err := r.handleMsgSubscriptionData(message); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - case message.Cmd.NodeManagementBindingRequestCall != nil: - if err := r.handleMsgBindingRequestCall(message, message.Cmd.NodeManagementBindingRequestCall); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - case message.Cmd.NodeManagementBindingDeleteCall != nil: - if err := r.handleMsgBindingDeleteCall(message, message.Cmd.NodeManagementBindingDeleteCall); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - case message.Cmd.NodeManagementBindingData != nil: - if err := r.handleMsgBindingData(message); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - case message.Cmd.NodeManagementUseCaseData != nil: - if err := r.handleMsgUseCaseData(message, message.Cmd.NodeManagementUseCaseData); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - case message.Cmd.NodeManagementDestinationListData != nil: - if err := r.handleMsgDestinationListData(message, message.Cmd.NodeManagementDestinationListData); err != nil { - return model.NewErrorType(model.ErrorNumberTypeGeneralError, err.Error()) - } - - default: - return model.NewErrorType(model.ErrorNumberTypeCommandNotSupported, fmt.Sprintf("nodemanagement.Handle: Cmd data not implemented: %s", message.Cmd.DataName())) - } - - return nil -} diff --git a/spine/nodemanagement_binding.go b/spine/nodemanagement_binding.go deleted file mode 100644 index 68d9eaac..00000000 --- a/spine/nodemanagement_binding.go +++ /dev/null @@ -1,80 +0,0 @@ -package spine - -import ( - "fmt" - - "github.com/ahmetb/go-linq/v3" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -func NewNodeManagementBindingRequestCallType(clientAddress *model.FeatureAddressType, serverAddress *model.FeatureAddressType, featureType model.FeatureTypeType) *model.NodeManagementBindingRequestCallType { - return &model.NodeManagementBindingRequestCallType{ - BindingRequest: &model.BindingManagementRequestCallType{ - ClientAddress: clientAddress, - ServerAddress: serverAddress, - ServerFeatureType: &featureType, - }, - } -} - -func NewNodeManagementBindingDeleteCallType(clientAddress *model.FeatureAddressType, serverAddress *model.FeatureAddressType) *model.NodeManagementBindingDeleteCallType { - return &model.NodeManagementBindingDeleteCallType{ - BindingDelete: &model.BindingManagementDeleteCallType{ - ClientAddress: clientAddress, - ServerAddress: serverAddress, - }, - } -} - -// route bindings request calls to the appropriate feature implementation and add the bindings to the current list -func (r *NodeManagementImpl) processReadBindingData(message *Message) error { - - var remoteDeviceBindings []model.BindingManagementEntryDataType - remoteDeviceBindingEntries := r.Device().BindingManager().Bindings(message.FeatureRemote.Device()) - linq.From(remoteDeviceBindingEntries).SelectT(func(s *BindingEntry) model.BindingManagementEntryDataType { - return model.BindingManagementEntryDataType{ - BindingId: util.Ptr(model.BindingIdType(s.id)), - ServerAddress: s.serverFeature.Address(), - ClientAddress: s.clientFeature.Address(), - } - }).ToSlice(&remoteDeviceBindings) - - cmd := model.CmdType{ - NodeManagementBindingData: &model.NodeManagementBindingDataType{ - BindingEntry: remoteDeviceBindings, - }, - } - - return message.FeatureRemote.Sender().Reply(message.RequestHeader, r.Address(), cmd) -} - -func (r *NodeManagementImpl) handleMsgBindingData(message *Message) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeCall: - return r.processReadBindingData(message) - - default: - return fmt.Errorf("nodemanagement.handleBindingDeleteCall: NodeManagementBindingRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} - -func (r *NodeManagementImpl) handleMsgBindingRequestCall(message *Message, data *model.NodeManagementBindingRequestCallType) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeCall: - return r.Device().BindingManager().AddBinding(message.FeatureRemote.Device(), *data.BindingRequest) - - default: - return fmt.Errorf("nodemanagement.handleBindingRequestCall: NodeManagementBindingRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} - -func (r *NodeManagementImpl) handleMsgBindingDeleteCall(message *Message, data *model.NodeManagementBindingDeleteCallType) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeCall: - return r.Device().BindingManager().RemoveBinding(*data.BindingDelete, message.FeatureRemote.Device()) - - default: - return fmt.Errorf("nodemanagement.handleBindingDeleteCall: NodeManagementBindingRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} diff --git a/spine/nodemanagement_destinationlist.go b/spine/nodemanagement_destinationlist.go deleted file mode 100644 index b7a89ea1..00000000 --- a/spine/nodemanagement_destinationlist.go +++ /dev/null @@ -1,50 +0,0 @@ -package spine - -import ( - "errors" - "fmt" - - "github.com/enbility/eebus-go/spine/model" -) - -func (r *NodeManagementImpl) RequestDestinationListData(remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *model.ErrorType) { - return nil, model.NewErrorTypeFromString("Not implemented") -} - -func (r *NodeManagementImpl) processReadDestinationListData(featureRemote FeatureRemote, requestHeader *model.HeaderType) error { - data := []model.NodeManagementDestinationDataType{ - r.Device().DestinationData(), - } - // add other remote devices here - - cmd := model.CmdType{ - NodeManagementDestinationListData: &model.NodeManagementDestinationListDataType{ - NodeManagementDestinationData: data, - }, - } - - return featureRemote.Sender().Reply(requestHeader, r.Address(), cmd) -} - -func (r *NodeManagementImpl) processReplyDestinationListData(message *Message, data model.NodeManagementDestinationListDataType) error { - return errors.New("Not implemented") -} - -func (r *NodeManagementImpl) handleMsgDestinationListData(message *Message, data *model.NodeManagementDestinationListDataType) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeRead: - return r.processReadDestinationListData(message.FeatureRemote, message.RequestHeader) - - case model.CmdClassifierTypeReply: - if err := r.pendingRequests.Remove(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference); err != nil { - return errors.New(err.String()) - } - return r.processReplyDestinationListData(message, *data) - - case model.CmdClassifierTypeNotify: - return r.processReplyDestinationListData(message, *data) - - default: - return fmt.Errorf("nodemanagement.handleMsgDestinationListData: NodeManagementDestinationListDataType CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} diff --git a/spine/nodemanagement_detaileddiscovery.go b/spine/nodemanagement_detaileddiscovery.go deleted file mode 100644 index 992f37f8..00000000 --- a/spine/nodemanagement_detaileddiscovery.go +++ /dev/null @@ -1,265 +0,0 @@ -package spine - -import ( - "errors" - "fmt" - - "github.com/enbility/eebus-go/spine/model" -) - -// request detailed discovery data from a remote device -func (r *NodeManagementImpl) RequestDetailedDiscovery(remoteDeviceSki string, remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *model.ErrorType) { - rfAdress := featureAddressType(NodeManagementFeatureId, EntityAddressType(remoteDeviceAddress, DeviceInformationAddressEntity)) - cmd := model.CmdType{ - NodeManagementDetailedDiscoveryData: &model.NodeManagementDetailedDiscoveryDataType{}, - } - return r.RequestDataBySenderAddress(cmd, sender, remoteDeviceSki, rfAdress, defaultMaxResponseDelay) -} - -// handle incoming detailed discovery read call -func (r *NodeManagementImpl) processReadDetailedDiscoveryData(deviceRemote DeviceRemote, requestHeader *model.HeaderType) error { - if deviceRemote == nil { - return errors.New("nodemanagement.readDetailedDiscoveryData: invalid deviceRemote") - } - - var entityInformation []model.NodeManagementDetailedDiscoveryEntityInformationType - var featureInformation []model.NodeManagementDetailedDiscoveryFeatureInformationType - - for _, e := range r.Device().Entities() { - entityInformation = append(entityInformation, *e.Information()) - - for _, f := range e.Features() { - featureInformation = append(featureInformation, *f.Information()) - } - } - - cmd := model.CmdType{ - NodeManagementDetailedDiscoveryData: &model.NodeManagementDetailedDiscoveryDataType{ - SpecificationVersionList: &model.NodeManagementSpecificationVersionListType{ - SpecificationVersion: []model.SpecificationVersionDataType{model.SpecificationVersionDataType(SpecificationVersion)}, - }, - DeviceInformation: r.Device().Information(), - EntityInformation: entityInformation, - FeatureInformation: featureInformation, - }, - } - - return deviceRemote.Sender().Reply(requestHeader, r.Address(), cmd) -} - -// handle incoming detailed discovery reply data -func (r *NodeManagementImpl) processReplyDetailedDiscoveryData(message *Message, data *model.NodeManagementDetailedDiscoveryDataType) error { - remoteDevice := message.DeviceRemote - - deviceDescription := data.DeviceInformation.Description - if deviceDescription == nil { - return errors.New("nodemanagement.replyDetailedDiscoveryData: invalid DeviceInformation.Description") - } - - remoteDevice.UpdateDevice(deviceDescription) - entities, err := remoteDevice.AddEntityAndFeatures(true, data) - if err != nil { - return err - } - - // publish event for remote device added - payload := EventPayload{ - Ski: remoteDevice.Ski(), - EventType: EventTypeDeviceChange, - ChangeType: ElementChangeAdd, - Device: remoteDevice, - Feature: message.FeatureRemote, - Data: data, - } - Events.Publish(payload) - - // publish event for each added remote entity - for _, entity := range entities { - payload := EventPayload{ - Ski: remoteDevice.Ski(), - EventType: EventTypeEntityChange, - ChangeType: ElementChangeAdd, - Device: remoteDevice, - Entity: entity, - Data: data, - } - Events.Publish(payload) - } - - return nil -} - -// handle incoming detailed discovery notify data -func (r *NodeManagementImpl) processNotifyDetailedDiscoveryData(message *Message, data *model.NodeManagementDetailedDiscoveryDataType) error { - // is this a partial request? - if message.FilterPartial == nil { - return errors.New("the received NodeManagementDetailedDiscovery.notify dataset should be partial") - } - - if data.EntityInformation == nil || len(data.EntityInformation) == 0 || data.EntityInformation[0].Description == nil || data.EntityInformation[0].Description.LastStateChange == nil { - return errors.New("the received NodeManagementDetailedDiscovery.notify dataset is incomplete") - } - - lastStateChange := *data.EntityInformation[0].Description.LastStateChange - remoteDevice := message.FeatureRemote.Device() - - // addition example: - // {"data":[{"header":[{"protocolId":"ee1.0"}]},{"payload":{"datagram":[{"header":[{"specificationVersion":"1.1.1"},{"addressSource":[{"device":"d:_i:19667_PorscheEVSE-00016544"},{"entity":[0]},{"feature":0}]},{"addressDestination":[{"device":"EVCC_HEMS"},{"entity":[0]},{"feature":0}]},{"msgCounter":926685},{"cmdClassifier":"notify"}]},{"payload":[{"cmd":[[{"function":"nodeManagementDetailedDiscoveryData"},{"filter":[[{"cmdControl":[{"partial":[]}]}]]},{"nodeManagementDetailedDiscoveryData":[{"deviceInformation":[{"description":[{"deviceAddress":[{"device":"d:_i:19667_PorscheEVSE-00016544"}]}]}]},{"entityInformation":[[{"description":[{"entityAddress":[{"entity":[1,1]}]},{"entityType":"EV"},{"lastStateChange":"added"},{"description":"Electric Vehicle"}]}]]},{"featureInformation":[[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":1}]},{"featureType":"LoadControl"},{"role":"server"},{"supportedFunction":[[{"function":"loadControlLimitDescriptionListData"},{"possibleOperations":[{"read":[]}]}],[{"function":"loadControlLimitListData"},{"possibleOperations":[{"read":[]},{"write":[]}]}]]},{"description":"Load Control"}]}],[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":2}]},{"featureType":"ElectricalConnection"},{"role":"server"},{"supportedFunction":[[{"function":"electricalConnectionParameterDescriptionListData"},{"possibleOperations":[{"read":[]}]}],[{"function":"electricalConnectionDescriptionListData"},{"possibleOperations":[{"read":[]}]}],[{"function":"electricalConnectionPermittedValueSetListData"},{"possibleOperations":[{"read":[]}]}]]},{"description":"Electrical Connection"}]}],[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":3}]},{"featureType":"Measurement"},{"specificUsage":["Electrical"]},{"role":"server"},{"supportedFunction":[[{"function":"measurementListData"},{"possibleOperations":[{"read":[]}]}],[{"function":"measurementDescriptionListData"},{"possibleOperations":[{"read":[]}]}]]},{"description":"Measurements"}]}],[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":5}]},{"featureType":"DeviceConfiguration"},{"role":"server"},{"supportedFunction":[[{"function":"deviceConfigurationKeyValueDescriptionListData"},{"possibleOperations":[{"read":[]}]}],[{"function":"deviceConfigurationKeyValueListData"},{"possibleOperations":[{"read":[]}]}]]},{"description":"Device Configuration EV"}]}],[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":6}]},{"featureType":"DeviceClassification"},{"role":"server"},{"supportedFunction":[[{"function":"deviceClassificationManufacturerData"},{"possibleOperations":[{"read":[]}]}]]},{"description":"Device Classification for EV"}]}],[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":7}]},{"featureType":"TimeSeries"},{"role":"server"},{"supportedFunction":[[{"function":"timeSeriesConstraintsListData"},{"possibleOperations":[{"read":[]}]}],[{"function":"timeSeriesDescriptionListData"},{"possibleOperations":[{"read":[]}]}],[{"function":"timeSeriesListData"},{"possibleOperations":[{"read":[]},{"write":[]}]}]]},{"description":"Time Series"}]}],[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":8}]},{"featureType":"IncentiveTable"},{"role":"server"},{"supportedFunction":[[{"function":"incentiveTableConstraintsData"},{"possibleOperations":[{"read":[]}]}],[{"function":"incentiveTableData"},{"possibleOperations":[{"read":[]},{"write":[]}]}],[{"function":"incentiveTableDescriptionData"},{"possibleOperations":[{"read":[]},{"write":[]}]}]]},{"description":"Incentive Table"}]}],[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":9}]},{"featureType":"DeviceDiagnosis"},{"role":"server"},{"supportedFunction":[[{"function":"deviceDiagnosisStateData"},{"possibleOperations":[{"read":[]}]}]]},{"description":"Device Diagnosis EV"}]}],[{"description":[{"featureAddress":[{"entity":[1,1]},{"feature":10}]},{"featureType":"Identification"},{"role":"server"},{"supportedFunction":[[{"function":"identificationListData"},{"possibleOperations":[{"read":[]}]}]]},{"description":"Identification for EV"}]}]]}]}]]}]}]}}]} - // { - // "cmd":[[ - // {"function":"nodeManagementDetailedDiscoveryData"}, - // {"filter":[[{"cmdControl":[{"partial":[]}]}]]}, - // {"nodeManagementDetailedDiscoveryData":[ - // {"deviceInformation":[{"description":[{"deviceAddress":[{"device":"d:_i:19667_PorscheEVSE-00016544"}]}]}]}, - // {"entityInformation":[[ - // {"description":[ - // {"entityAddress":[{"entity":[1,1]}]}, - // {"entityType":"EV"}, - // {"lastStateChange":"added"}, - // {"description":"Electric Vehicle"} - // ]} - // ]]}, - // {"featureInformation":[ - // [{"description":[ - // {"featureAddress":[{"entity":[1,1]},{"feature":1}]}, - // {"featureType":"LoadControl"}, - // {"role":"server"}, - // {"supportedFunction":[ - // [{"function":"loadControlLimitDescriptionListData"},{"possibleOperations":[{"read":[]}]}], - // [{"function":"loadControlLimitListData"},{"possibleOperations":[{"read":[]},{"write":[]}]}] - // ]}, - // {"description":"Load Control"} - // ]}], - // ... - - // is this addition? - if lastStateChange == model.NetworkManagementStateChangeTypeAdded { - entities, err := remoteDevice.AddEntityAndFeatures(false, data) - if err != nil { - return err - } - - // publish event for each added remote entity - for _, entity := range entities { - payload := EventPayload{ - Ski: remoteDevice.Ski(), - EventType: EventTypeEntityChange, - ChangeType: ElementChangeAdd, - Device: remoteDevice, - Entity: entity, - Data: data, - } - Events.Publish(payload) - } - } - - // removal example: - // {"data":[{"header":[{"protocolId":"ee1.0"}]},{"payload":{"datagram":[{"header":[{"specificationVersion":"1.1.1"},{"addressSource":[{"device":"d:_i:19667_PorscheEVSE-00016544"},{"entity":[0]},{"feature":0}]},{"addressDestination":[{"device":"EVCC_HEMS"},{"entity":[0]},{"feature":0}]},{"msgCounter":4835},{"cmdClassifier":"notify"}]},{"payload":[{"cmd":[[{"function":"nodeManagementDetailedDiscoveryData"},{"filter":[[{"cmdControl":[{"partial":[]}]}]]},{"nodeManagementDetailedDiscoveryData":[{"deviceInformation":[{"description":[{"deviceAddress":[{"device":"d:_i:19667_PorscheEVSE-00016544"}]}]}]},{"entityInformation":[[{"description":[{"entityAddress":[{"entity":[1,1]}]},{"lastStateChange":"removed"}]}]]}]}]]}]}]}}]} - // { - // "cmd": [[ - // {"function": "nodeManagementDetailedDiscoveryData"}, - // {"filter": [[{"cmdControl": [{"partial": []}]}]]}, - // {"nodeManagementDetailedDiscoveryData": [ - // {"deviceInformation": [{"description": [{"deviceAddress": [{"device": "d:_i:19667_PorscheEVSE-00016544"}]}]}]}, - // {"entityInformation": [[ - // { - // "description": [ - // {"entityAddress": [{"entity": [1,1]}]}, - // {"lastStateChange": "removed"} - // ... - - // is this removal? - if lastStateChange == model.NetworkManagementStateChangeTypeRemoved { - for _, ei := range data.EntityInformation { - if err := remoteDevice.CheckEntityInformation(false, ei); err != nil { - return err - } - - entityAddress := ei.Description.EntityAddress.Entity - removedEntity := remoteDevice.RemoveByAddress(entityAddress) - - // only continue if the entity existed - if removedEntity == nil { - continue - } - - payload := EventPayload{ - Ski: remoteDevice.Ski(), - EventType: EventTypeEntityChange, - ChangeType: ElementChangeRemove, - Device: remoteDevice, - Entity: removedEntity, - Data: data, - } - Events.Publish(payload) - - // remove all subscriptions for this entity - subscriptionMgr := r.Device().SubscriptionManager() - subscriptionMgr.RemoveSubscriptionsForEntity(removedEntity) - - // make sure Heartbeat Manager is up to date - r.Device().HeartbeatManager().UpdateHeartbeatOnSubscriptions() - - // remove all bindings for this entity - bindingMgr := r.Device().BindingManager() - bindingMgr.RemoveBindingsForEntity(removedEntity) - } - } - - return nil -} - -// func (f *NodeManagement) announceFeatureDiscovery(e Entity) error { -// entity := f.Entity() -// if entity == nil { -// return errors.New("announceFeatureDiscovery: entity not found") -// } -// device := entity.Device() -// if device == nil { -// return errors.New("announceFeatureDiscovery: device not found") -// } -// entities := device.Entities() -// if entities == nil { -// return errors.New("announceFeatureDiscovery: entities not found") -// } - -// for _, le := range entities { -// for _, lf := range le.Features() { - -// // connect client to server features -// for _, rf := range e.Features() { -// lr := lf.Role() -// rr := rf.Role() -// rolesValid := (lr == model.RoleTypeSpecial && rr == model.RoleTypeSpecial) || (lr == model.RoleTypeClient && rr == model.RoleTypeServer) -// if lf.Type() == rf.Type() && rolesValid { -// if cf, ok := lf.(ClientFeature); ok { -// if err := cf.ServerFound(rf); err != nil { -// return err -// } -// } -// } -// } -// } -// } - -// return nil -// } - -func (r *NodeManagementImpl) handleMsgDetailedDiscoveryData(message *Message, data *model.NodeManagementDetailedDiscoveryDataType) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeRead: - return r.processReadDetailedDiscoveryData(message.DeviceRemote, message.RequestHeader) - - case model.CmdClassifierTypeReply: - if err := r.pendingRequests.Remove(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference); err != nil { - return errors.New(err.String()) - } - return r.processReplyDetailedDiscoveryData(message, data) - - case model.CmdClassifierTypeNotify: - return r.processNotifyDetailedDiscoveryData(message, data) - - default: - return fmt.Errorf("nodemanagement.handleDetailedDiscoveryData: NodeManagementDetailedDiscoveryData CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} diff --git a/spine/nodemanagement_detaileddiscovery_test.go b/spine/nodemanagement_detaileddiscovery_test.go deleted file mode 100644 index dfa5a984..00000000 --- a/spine/nodemanagement_detaileddiscovery_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -const ( - nm_detaileddiscoverydata_send_read_file_prefix = "./testdata/nm_detaileddiscoverydata_send_read" - nm_detaileddiscoverydata_recv_read_file_path = "./testdata/nm_detaileddiscoverydata_recv_read.json" - nm_detaileddiscoverydata_send_reply_file_prefix = "./testdata/nm_detaileddiscoverydata_send_reply" - nm_detaileddiscoverydata_recv_read_ack_file_path = "./testdata/nm_detaileddiscoverydata_recv_read_ack.json" - nm_detaileddiscoverydata_send_result_file_prefix = "./testdata/nm_detaileddiscoverydata_send_result" - nm_subscriptionRequestCall_recv_call_file_path = "./testdata/nm_subscriptionRequestCall_recv_call.json" - nm_subscriptionRequestCall_send_result_file_prefix = "./testdata/nm_subscriptionRequestCall_send_result" - nm_destinationListData_recv_read_file_path = "./testdata/nm_destinationListData_recv_read.json" - nm_destinationListData_send_reply_file_prefix = "./testdata/nm_destinationListData_send_reply" -) - -func TestNodeManagementSuite(t *testing.T) { - suite.Run(t, new(NodeManagementSuite)) -} - -type NodeManagementSuite struct { - suite.Suite - sut DeviceLocal - - remoteSki string - - writeHandler *WriteMessageHandler - remoteDevice DeviceRemote -} - -func (s *NodeManagementSuite) BeforeTest(suiteName, testName string) { - s.sut = NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", - "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - s.remoteSki = "TestRemoteSki" - - s.writeHandler = &WriteMessageHandler{} - _ = s.sut.SetupRemoteDevice(s.remoteSki, s.writeHandler) - s.remoteDevice = s.sut.RemoteDeviceForSki(s.remoteSki) -} - -func (s *NodeManagementSuite) TestDetailedDiscovery_SendRead() { - // Act (see BeforeTest) - - // Assert - sendBytes := s.writeHandler.LastMessage() - checkSentData(s.T(), sendBytes, nm_detaileddiscoverydata_send_read_file_prefix) -} - -func (s *NodeManagementSuite) TestDetailedDiscovery_SendReply() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_detaileddiscoverydata_recv_read_file_path)) - - // Assert - sendBytes := s.writeHandler.MessageWithReference(msgCounter) - checkSentData(s.T(), sendBytes, nm_detaileddiscoverydata_send_reply_file_prefix) -} - -func (s *NodeManagementSuite) TestDetailedDiscovery_RecvReply() { - // Act - _, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_reply_file_path)) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - assert.Equal(s.T(), model.DeviceTypeTypeChargingStation, *remoteDevice.DeviceType()) - assert.Equal(s.T(), model.NetworkManagementFeatureSetTypeSmart, *remoteDevice.FeatureSet()) - - rEntities := remoteDevice.Entities() - assert.Equal(s.T(), 2, len(rEntities)) - di := rEntities[DeviceInformationEntityId] - assert.NotNil(s.T(), di) - assert.Equal(s.T(), model.EntityTypeTypeDeviceInformation, di.EntityType()) - - diFeatures := di.Features() - assert.Equal(s.T(), 2, len(diFeatures)) - - nm := diFeatures[0] - assert.Equal(s.T(), NodeManagementFeatureId, uint(*nm.Address().Feature)) - assert.Equal(s.T(), model.FeatureTypeTypeNodeManagement, nm.Type()) - assert.Equal(s.T(), model.RoleTypeSpecial, nm.Role()) - assert.Equal(s.T(), 8, len(nm.Operations())) - - dc := diFeatures[1] - assert.Equal(s.T(), 1, int(*dc.Address().Feature)) - assert.Equal(s.T(), model.FeatureTypeTypeDeviceClassification, dc.Type()) - assert.Equal(s.T(), model.RoleTypeServer, dc.Role()) - assert.Equal(s.T(), 1, len(dc.Operations())) - - evse := rEntities[1] - assert.NotNil(s.T(), evse) - assert.Equal(s.T(), model.EntityTypeTypeEVSE, evse.EntityType()) - - evseFeatures := evse.Features() - assert.Equal(s.T(), 3, len(evseFeatures)) - - evsedc := evseFeatures[0] - assert.Equal(s.T(), 1, int(*evsedc.Address().Feature)) - assert.Equal(s.T(), model.FeatureTypeTypeDeviceClassification, evsedc.Type()) - assert.Equal(s.T(), model.RoleTypeClient, evsedc.Role()) - assert.Equal(s.T(), 0, len(evsedc.Operations())) - - evsedd := evseFeatures[1] - assert.Equal(s.T(), 2, int(*evsedd.Address().Feature)) - assert.Equal(s.T(), model.FeatureTypeTypeDeviceDiagnosis, evsedd.Type()) - assert.Equal(s.T(), model.RoleTypeClient, evsedd.Role()) - assert.Equal(s.T(), 0, len(evsedd.Operations())) - - evseec := evseFeatures[2] - assert.Equal(s.T(), 3, int(*evseec.Address().Feature)) - assert.Equal(s.T(), model.FeatureTypeTypeElectricalConnection, evseec.Type()) - assert.Equal(s.T(), model.RoleTypeServer, evseec.Role()) - assert.Equal(s.T(), 0, len(evseec.Operations())) - -} - -func (s *NodeManagementSuite) TestDetailedDiscovery_RecvNotifyAdded() { - _, _ = s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_reply_file_path)) - - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), wallbox_detaileddiscoverydata_recv_notify_file_path)) - waitForAck(s.T(), msgCounter, s.writeHandler) - - // Assert - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - assert.NotNil(s.T(), remoteDevice) - assert.Equal(s.T(), model.DeviceTypeTypeChargingStation, *remoteDevice.DeviceType()) - assert.Equal(s.T(), model.NetworkManagementFeatureSetTypeSmart, *remoteDevice.FeatureSet()) - - rEntities := remoteDevice.Entities() - if assert.Equal(s.T(), 3, len(rEntities)) { - { - di := rEntities[DeviceInformationEntityId] - assert.NotNil(s.T(), di) - assert.Equal(s.T(), model.EntityTypeTypeDeviceInformation, di.EntityType()) - assert.Equal(s.T(), 2, len(di.Features())) - } - { - evse := rEntities[1] - assert.NotNil(s.T(), evse) - assert.Equal(s.T(), model.EntityTypeTypeEVSE, evse.EntityType()) - assert.Equal(s.T(), 3, len(evse.Features())) - } - { - ev := rEntities[2] - assert.NotNil(s.T(), ev) - assert.Equal(s.T(), model.EntityTypeTypeEV, ev.EntityType()) - assert.Equal(s.T(), 10, len(ev.Features())) - } - } -} - -func (s *NodeManagementSuite) TestDetailedDiscovery_SendReplyWithAcknowledge() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_detaileddiscoverydata_recv_read_ack_file_path)) - - // Assert - sentReply := s.writeHandler.MessageWithReference(msgCounter) - checkSentData(s.T(), sentReply, nm_detaileddiscoverydata_send_reply_file_prefix) - sentResult := s.writeHandler.ResultWithReference(msgCounter) - checkSentData(s.T(), sentResult, nm_detaileddiscoverydata_send_result_file_prefix) -} - -func (s *NodeManagementSuite) TestSubscriptionRequestCall_BeforeDetailedDiscovery() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_subscriptionRequestCall_recv_call_file_path)) - - // Assert - sentResult := s.writeHandler.ResultWithReference(msgCounter) - checkSentData(s.T(), sentResult, nm_subscriptionRequestCall_send_result_file_prefix) - - remoteDevice := s.sut.RemoteDeviceForSki(s.remoteSki) - subscriptionsForDevice := s.sut.SubscriptionManager().Subscriptions(remoteDevice) - assert.Equal(s.T(), 1, len(subscriptionsForDevice)) - subscriptionsOnFeature := s.sut.SubscriptionManager().SubscriptionsOnFeature(*NodeManagementAddress(s.sut.Address())) - assert.Equal(s.T(), 1, len(subscriptionsOnFeature)) -} - -func (s *NodeManagementSuite) TestDestinationList_SendReply() { - // Act - msgCounter, _ := s.remoteDevice.HandleSpineMesssage(loadFileData(s.T(), nm_destinationListData_recv_read_file_path)) - - // Assert - sendBytes := s.writeHandler.MessageWithReference(msgCounter) - checkSentData(s.T(), sendBytes, nm_destinationListData_send_reply_file_prefix) -} diff --git a/spine/nodemanagement_subscription.go b/spine/nodemanagement_subscription.go deleted file mode 100644 index a221fb24..00000000 --- a/spine/nodemanagement_subscription.go +++ /dev/null @@ -1,93 +0,0 @@ -package spine - -import ( - "fmt" - - "github.com/ahmetb/go-linq/v3" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -func NewNodeManagementSubscriptionRequestCallType(clientAddress *model.FeatureAddressType, serverAddress *model.FeatureAddressType, featureType model.FeatureTypeType) *model.NodeManagementSubscriptionRequestCallType { - return &model.NodeManagementSubscriptionRequestCallType{ - SubscriptionRequest: &model.SubscriptionManagementRequestCallType{ - ClientAddress: clientAddress, - ServerAddress: serverAddress, - ServerFeatureType: &featureType, - }, - } -} - -func NewNodeManagementSubscriptionDeleteCallType(clientAddress *model.FeatureAddressType, serverAddress *model.FeatureAddressType) *model.NodeManagementSubscriptionDeleteCallType { - return &model.NodeManagementSubscriptionDeleteCallType{ - SubscriptionDelete: &model.SubscriptionManagementDeleteCallType{ - ClientAddress: clientAddress, - ServerAddress: serverAddress, - }, - } -} - -// route subscription request calls to the appropriate feature implementation and add the subscription to the current list -func (r *NodeManagementImpl) processReadSubscriptionData(message *Message) error { - - var remoteDeviceSubscriptions []model.SubscriptionManagementEntryDataType - remoteDeviceSubscriptionEntries := r.Device().SubscriptionManager().Subscriptions(message.FeatureRemote.Device()) - linq.From(remoteDeviceSubscriptionEntries).SelectT(func(s *SubscriptionEntry) model.SubscriptionManagementEntryDataType { - return model.SubscriptionManagementEntryDataType{ - SubscriptionId: util.Ptr(model.SubscriptionIdType(s.id)), - ServerAddress: s.serverFeature.Address(), - ClientAddress: s.clientFeature.Address(), - } - }).ToSlice(&remoteDeviceSubscriptions) - - cmd := model.CmdType{ - NodeManagementSubscriptionData: &model.NodeManagementSubscriptionDataType{ - SubscriptionEntry: remoteDeviceSubscriptions, - }, - } - - return message.FeatureRemote.Sender().Reply(message.RequestHeader, r.Address(), cmd) -} - -func (r *NodeManagementImpl) handleMsgSubscriptionData(message *Message) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeCall: - return r.processReadSubscriptionData(message) - - default: - return fmt.Errorf("nodemanagement.handleSubscriptionDeleteCall: NodeManagementSubscriptionRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} - -func (r *NodeManagementImpl) handleMsgSubscriptionRequestCall(message *Message, data *model.NodeManagementSubscriptionRequestCallType) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeCall: - subscriptionMgr := r.Device().SubscriptionManager() - - err := subscriptionMgr.AddSubscription(message.FeatureRemote.Device(), *data.SubscriptionRequest) - if err == nil { - r.Device().HeartbeatManager().UpdateHeartbeatOnSubscriptions() - } - - return err - - default: - return fmt.Errorf("nodemanagement.handleSubscriptionRequestCall: NodeManagementSubscriptionRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} - -func (r *NodeManagementImpl) handleMsgSubscriptionDeleteCall(message *Message, data *model.NodeManagementSubscriptionDeleteCallType) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeCall: - subscriptionMgr := r.Device().SubscriptionManager() - - err := subscriptionMgr.RemoveSubscription(*data.SubscriptionDelete, message.FeatureRemote.Device()) - if err == nil { - r.Device().HeartbeatManager().UpdateHeartbeatOnSubscriptions() - } - - return err - default: - return fmt.Errorf("nodemanagement.handleSubscriptionDeleteCall: NodeManagementSubscriptionRequestCall CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} diff --git a/spine/nodemanagement_test.go b/spine/nodemanagement_test.go deleted file mode 100644 index d0000eb8..00000000 --- a/spine/nodemanagement_test.go +++ /dev/null @@ -1,155 +0,0 @@ -package spine - -import ( - "reflect" - "testing" - - "github.com/enbility/eebus-go/spine/mocks" - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestNodemanagement_BindingCalls(t *testing.T) { - const bindingEntityId uint = 1 - const featureType = model.FeatureTypeTypeLoadControl - - senderMock := mocks.NewSender(t) - - _, serverFeature := createLocalDeviceAndFeature(bindingEntityId, featureType) - clientFeature, _ := createRemoteDeviceAndFeature(bindingEntityId, featureType, senderMock) - - senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - cmd := args.Get(2).(model.CmdType) - assert.Equal(t, 1, len(cmd.NodeManagementBindingData.BindingEntry)) - assert.True(t, reflect.DeepEqual(cmd.NodeManagementBindingData.BindingEntry[0].ClientAddress, clientFeature.Address())) - assert.True(t, reflect.DeepEqual(cmd.NodeManagementBindingData.BindingEntry[0].ServerAddress, serverFeature.Address())) - }).Return(nil).Once() - - requestMsg := Message{ - Cmd: model.CmdType{ - NodeManagementBindingRequestCall: NewNodeManagementBindingRequestCallType( - clientFeature.Address(), serverFeature.Address(), featureType), - }, - CmdClassifier: model.CmdClassifierTypeCall, - FeatureRemote: clientFeature, - } - - sut := NewNodeManagementImpl(0, serverFeature.Entity()) - - // Act - err := sut.HandleMessage(&requestMsg) - if assert.Nil(t, err) { - - dataMsg := Message{ - Cmd: model.CmdType{ - NodeManagementBindingData: &model.NodeManagementBindingDataType{}, - }, - CmdClassifier: model.CmdClassifierTypeCall, - FeatureRemote: clientFeature, - } - err = sut.HandleMessage(&dataMsg) - assert.Nil(t, err) - } - - senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - cmd := args.Get(2).(model.CmdType) - assert.Equal(t, 0, len(cmd.NodeManagementBindingData.BindingEntry)) - }).Return(nil).Once() - - deleteMsg := Message{ - Cmd: model.CmdType{ - NodeManagementBindingDeleteCall: NewNodeManagementBindingDeleteCallType( - clientFeature.Address(), serverFeature.Address()), - }, - CmdClassifier: model.CmdClassifierTypeCall, - FeatureRemote: clientFeature, - } - - // Act - err = sut.HandleMessage(&deleteMsg) - if assert.Nil(t, err) { - - dataMsg := Message{ - Cmd: model.CmdType{ - NodeManagementBindingData: &model.NodeManagementBindingDataType{}, - }, - CmdClassifier: model.CmdClassifierTypeCall, - FeatureRemote: clientFeature, - } - err = sut.HandleMessage(&dataMsg) - assert.Nil(t, err) - } -} - -func TestNodemanagement_SubscriptionCalls(t *testing.T) { - const subscriptionEntityId uint = 1 - const featureType = model.FeatureTypeTypeDeviceClassification - - senderMock := mocks.NewSender(t) - - _, serverFeature := createLocalDeviceAndFeature(subscriptionEntityId, featureType) - clientFeature, _ := createRemoteDeviceAndFeature(subscriptionEntityId, featureType, senderMock) - - senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - cmd := args.Get(2).(model.CmdType) - assert.Equal(t, 1, len(cmd.NodeManagementSubscriptionData.SubscriptionEntry)) - assert.True(t, reflect.DeepEqual(cmd.NodeManagementSubscriptionData.SubscriptionEntry[0].ClientAddress, clientFeature.Address())) - assert.True(t, reflect.DeepEqual(cmd.NodeManagementSubscriptionData.SubscriptionEntry[0].ServerAddress, serverFeature.Address())) - }).Return(nil).Once() - - requestMsg := Message{ - Cmd: model.CmdType{ - NodeManagementSubscriptionRequestCall: NewNodeManagementSubscriptionRequestCallType( - clientFeature.Address(), serverFeature.Address(), featureType), - }, - CmdClassifier: model.CmdClassifierTypeCall, - FeatureRemote: clientFeature, - } - - sut := NewNodeManagementImpl(0, serverFeature.Entity()) - - // Act - err := sut.HandleMessage(&requestMsg) - if assert.Nil(t, err) { - - dataMsg := Message{ - Cmd: model.CmdType{ - NodeManagementSubscriptionData: &model.NodeManagementSubscriptionDataType{}, - }, - CmdClassifier: model.CmdClassifierTypeCall, - FeatureRemote: clientFeature, - } - err = sut.HandleMessage(&dataMsg) - assert.Nil(t, err) - } - - senderMock.On("Reply", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - cmd := args.Get(2).(model.CmdType) - assert.Equal(t, 0, len(cmd.NodeManagementSubscriptionData.SubscriptionEntry)) - }).Return(nil).Once() - - deleteMsg := Message{ - Cmd: model.CmdType{ - NodeManagementSubscriptionDeleteCall: NewNodeManagementSubscriptionDeleteCallType( - clientFeature.Address(), serverFeature.Address()), - }, - CmdClassifier: model.CmdClassifierTypeCall, - FeatureRemote: clientFeature, - } - - // Act - err = sut.HandleMessage(&deleteMsg) - if assert.Nil(t, err) { - - dataMsg := Message{ - Cmd: model.CmdType{ - NodeManagementSubscriptionData: &model.NodeManagementSubscriptionDataType{}, - }, - CmdClassifier: model.CmdClassifierTypeCall, - FeatureRemote: clientFeature, - } - err = sut.HandleMessage(&dataMsg) - assert.Nil(t, err) - } -} diff --git a/spine/nodemanagement_usecase.go b/spine/nodemanagement_usecase.go deleted file mode 100644 index 3e1f4277..00000000 --- a/spine/nodemanagement_usecase.go +++ /dev/null @@ -1,73 +0,0 @@ -package spine - -import ( - "errors" - "fmt" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -func (r *NodeManagementImpl) RequestUseCaseData(remoteDeviceSki string, remoteDeviceAddress *model.AddressDeviceType, sender Sender) (*model.MsgCounterType, *model.ErrorType) { - rfAdress := featureAddressType(NodeManagementFeatureId, EntityAddressType(remoteDeviceAddress, DeviceInformationAddressEntity)) - cmd := model.CmdType{ - NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{}, - } - return r.RequestDataBySenderAddress(cmd, sender, remoteDeviceSki, rfAdress, defaultMaxResponseDelay) -} - -func (r *NodeManagementImpl) NotifyUseCaseData(remoteDevice DeviceRemote) (*model.MsgCounterType, error) { - rfAdress := featureAddressType(NodeManagementFeatureId, EntityAddressType(remoteDevice.Address(), DeviceInformationAddressEntity)) - rEntity := remoteDevice.Entity([]model.AddressEntityType{model.AddressEntityType(DeviceInformationEntityId)}) - - featureRemote := remoteDevice.FeatureByEntityTypeAndRole(rEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) - - fd := r.functionData(model.FunctionTypeNodeManagementUseCaseData) - cmd := fd.NotifyCmdType(nil, nil, false, nil) - - return featureRemote.Sender().Notify(r.Address(), rfAdress, cmd) -} - -func (r *NodeManagementImpl) processReadUseCaseData(featureRemote FeatureRemote, requestHeader *model.HeaderType) error { - cmd := r.functionData(model.FunctionTypeNodeManagementUseCaseData).ReplyCmdType(false) - - return featureRemote.Sender().Reply(requestHeader, r.Address(), cmd) -} - -func (r *NodeManagementImpl) processReplyUseCaseData(message *Message, data *model.NodeManagementUseCaseDataType) error { - message.FeatureRemote.UpdateData(model.FunctionTypeNodeManagementUseCaseData, data, nil, nil) - - // the data was updated, so send an event, other event handlers may watch out for this as well - payload := EventPayload{ - Ski: message.FeatureRemote.Device().Ski(), - EventType: EventTypeDataChange, - ChangeType: ElementChangeUpdate, - Feature: message.FeatureRemote, - Device: message.FeatureRemote.Device(), - Entity: message.FeatureRemote.Entity(), - CmdClassifier: util.Ptr(message.CmdClassifier), - Data: data, - } - Events.Publish(payload) - - return nil -} - -func (r *NodeManagementImpl) handleMsgUseCaseData(message *Message, data *model.NodeManagementUseCaseDataType) error { - switch message.CmdClassifier { - case model.CmdClassifierTypeRead: - return r.processReadUseCaseData(message.FeatureRemote, message.RequestHeader) - - case model.CmdClassifierTypeReply: - if err := r.pendingRequests.Remove(message.DeviceRemote.Ski(), *message.RequestHeader.MsgCounterReference); err != nil { - return errors.New(err.String()) - } - return r.processReplyUseCaseData(message, data) - - case model.CmdClassifierTypeNotify: - return r.processReplyUseCaseData(message, data) - - default: - return fmt.Errorf("nodemanagement.handleUseCaseData: NodeManagementUseCaseData CmdClassifierType not implemented: %s", message.CmdClassifier) - } -} diff --git a/spine/operations.go b/spine/operations.go deleted file mode 100644 index 504e344b..00000000 --- a/spine/operations.go +++ /dev/null @@ -1,39 +0,0 @@ -package spine - -import ( - "github.com/enbility/eebus-go/spine/model" -) - -type Operations struct { - Read, Write bool -} - -func NewOperations(read, write bool) *Operations { - return &Operations{ - Read: read, - Write: write, - } -} - -func (r *Operations) String() string { - switch { - case r.Read && !r.Write: - return "RO" - case r.Read && r.Write: - return "RW" - default: - return "--" - } -} - -func (r *Operations) Information() *model.PossibleOperationsType { - res := new(model.PossibleOperationsType) - if r.Read { - res.Read = &model.PossibleOperationsReadType{} - } - if r.Write { - res.Write = &model.PossibleOperationsWriteType{} - } - - return res -} diff --git a/spine/operations_test.go b/spine/operations_test.go deleted file mode 100644 index b9cbdbee..00000000 --- a/spine/operations_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package spine - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestOperations(t *testing.T) { - operations := NewOperations(true, false) - assert.NotNil(t, operations) - - text := operations.String() - assert.NotEqual(t, 0, len(text)) - - data := operations.Information() - assert.NotNil(t, data) - - operations2 := NewOperations(true, true) - assert.NotNil(t, operations2) - - text = operations2.String() - assert.NotEqual(t, 0, len(text)) - - data = operations2.Information() - assert.NotNil(t, data) - - operations3 := NewOperations(false, false) - assert.NotNil(t, operations3) - - text = operations3.String() - assert.NotEqual(t, 0, len(text)) - - data = operations3.Information() - assert.NotNil(t, data) -} diff --git a/spine/pending_requests.go b/spine/pending_requests.go deleted file mode 100644 index aa3e96e8..00000000 --- a/spine/pending_requests.go +++ /dev/null @@ -1,112 +0,0 @@ -package spine - -import ( - "fmt" - "sync" - "time" - - "github.com/enbility/eebus-go/spine/model" -) - -type dataErrorPair struct { - data any - errorResult *model.ErrorType -} - -type request struct { - ski string // can not use device, as this is not available for the very first requests! - counter model.MsgCounterType - countdown *time.Timer - response chan *dataErrorPair -} - -func (r *request) setTimeoutResult() { - if len(r.response) == 0 { - errorResult := model.NewErrorType(model.ErrorNumberTypeTimeout, fmt.Sprintf("the request with the message counter '%s' timed out", r.counter.String())) - r.response <- &dataErrorPair{data: nil, errorResult: errorResult} - } -} - -type PendingRequestsImpl struct { - requestMap sync.Map -} - -func NewPendingRequest() PendingRequests { - return &PendingRequestsImpl{ - requestMap: sync.Map{}, - } -} - -func (r *PendingRequestsImpl) Add(ski string, counter model.MsgCounterType, maxDelay time.Duration) { - newRequest := &request{ - ski: ski, - counter: counter, - // could be a performance problem in case of many requests - response: make(chan *dataErrorPair, 1), // buffered, so that SetData will not block, - } - newRequest.countdown = time.AfterFunc(maxDelay, func() { newRequest.setTimeoutResult() }) - - r.requestMap.Store(r.mapKey(ski, counter), newRequest) -} - -func (r *PendingRequestsImpl) SetData(ski string, counter model.MsgCounterType, data any) *model.ErrorType { - return r.setResponse(ski, counter, data, nil) -} - -func (r *PendingRequestsImpl) SetResult(ski string, counter model.MsgCounterType, errorResult *model.ErrorType) *model.ErrorType { - return r.setResponse(ski, counter, nil, errorResult) -} - -func (r *PendingRequestsImpl) GetData(ski string, counter model.MsgCounterType) (any, *model.ErrorType) { - request, err := r.getRequest(ski, counter) - if err != nil { - return nil, err - } - - data := <-request.response - r.removeRequest(request) - - return data.data, data.errorResult -} - -func (r *PendingRequestsImpl) Remove(ski string, counter model.MsgCounterType) *model.ErrorType { - request, err := r.getRequest(ski, counter) - if err != nil { - return err - } - r.removeRequest(request) - return nil -} - -func (r *PendingRequestsImpl) mapKey(ski string, counter model.MsgCounterType) string { - return fmt.Sprintf("%s:%d", ski, counter) -} - -func (r *PendingRequestsImpl) removeRequest(request *request) { - request.countdown.Stop() - r.requestMap.Delete(r.mapKey(request.ski, request.counter)) -} - -func (r *PendingRequestsImpl) getRequest(ski string, counter model.MsgCounterType) (*request, *model.ErrorType) { - rq, exists := r.requestMap.Load(r.mapKey(ski, counter)) - if !exists { - return nil, model.NewErrorTypeFromString(fmt.Sprintf("No pending request with message counter '%s' found", counter.String())) - } - - return rq.(*request), nil -} - -func (r *PendingRequestsImpl) setResponse(ski string, counter model.MsgCounterType, data any, errorResult *model.ErrorType) *model.ErrorType { - - request, err := r.getRequest(ski, counter) - if err != nil { - return err - } - if len(request.response) > 0 { - return model.NewErrorTypeFromString(fmt.Sprintf("the Data or Result for the request (MsgCounter: %s) was already set!", &counter)) - } - - request.countdown.Stop() - request.response <- &dataErrorPair{data: data, errorResult: errorResult} - return nil -} diff --git a/spine/pending_requests_test.go b/spine/pending_requests_test.go deleted file mode 100644 index b77ece98..00000000 --- a/spine/pending_requests_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type PendingRequestsTestSuite struct { - suite.Suite - sut PendingRequests - ski string - counter model.MsgCounterType -} - -func TestPendingRequestsSuite(t *testing.T) { - suite.Run(t, new(PendingRequestsTestSuite)) -} - -func (suite *PendingRequestsTestSuite) SetupSuite() { - suite.counter = model.MsgCounterType(1) - suite.sut = NewPendingRequest() - suite.ski = "test" -} - -func (suite *PendingRequestsTestSuite) SetupTest() { - suite.sut.Add(suite.ski, suite.counter, defaultMaxResponseDelay) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_Timeout() { - _ = suite.sut.Remove(suite.ski, suite.counter) - suite.sut.Add(suite.ski, suite.counter, time.Duration(time.Millisecond*10)) - - time.Sleep(time.Duration(time.Millisecond * 20)) - - // Act - data, err := suite.sut.GetData(suite.ski, suite.counter) - assert.Nil(suite.T(), data) - assert.NotNil(suite.T(), err) - assert.Equal(suite.T(), model.ErrorNumberTypeTimeout, err.ErrorNumber) - assert.Equal(suite.T(), "the request with the message counter '1' timed out", string(*err.Description)) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_Remove() { - // Act - err := suite.sut.Remove(suite.ski, suite.counter) - assert.Nil(suite.T(), err) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_Remove_GetData() { - _ = suite.sut.Remove(suite.ski, suite.counter) - - // Act - _, err := suite.sut.GetData(suite.ski, suite.counter) - assert.NotNil(suite.T(), err) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_SetData() { - // Act - err := suite.sut.SetData(suite.ski, suite.counter, 1) - assert.Nil(suite.T(), err) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_SetData_UnknownCounter() { - // Act - err := suite.sut.SetData(suite.ski, model.MsgCounterType(2), 1) - assert.NotNil(suite.T(), err) - assert.Equal(suite.T(), "No pending request with message counter '2' found", string(*err.Description)) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_SetData_SetData() { - _ = suite.sut.SetData(suite.ski, suite.counter, 1) - // Act - err := suite.sut.SetData(suite.ski, suite.counter, 2) - assert.NotNil(suite.T(), err) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_SetResult() { - // Act - err := suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorTypeFromString("unknown error")) - assert.Nil(suite.T(), err) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_SetResult_SetResult() { - _ = suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorTypeFromString("unknown error")) - // Act - err := suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorTypeFromString("unknown error")) - assert.NotNil(suite.T(), err) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_SetData_SetResult() { - _ = suite.sut.SetData(suite.ski, suite.counter, 1) - // Act - err := suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorTypeFromString("unknown error")) - assert.NotNil(suite.T(), err) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_SetData_GetData() { - data := 1 - _ = suite.sut.SetData(suite.ski, suite.counter, data) - - // Act - result, err := suite.sut.GetData(suite.ski, suite.counter) - assert.Nil(suite.T(), err) - assert.NotNil(suite.T(), result) - assert.Equal(suite.T(), data, result) -} - -func (suite *PendingRequestsTestSuite) TestPendingRequests_SetResult_GetData() { - errNo := model.ErrorNumberTypeTimeout - errDesc := "Timeout occurred" - _ = suite.sut.SetResult(suite.ski, suite.counter, model.NewErrorType(errNo, errDesc)) - - // Act - result, err := suite.sut.GetData(suite.ski, suite.counter) - assert.Nil(suite.T(), result) - assert.NotNil(suite.T(), err) - assert.Equal(suite.T(), errNo, err.ErrorNumber) - assert.Equal(suite.T(), errDesc, string(*err.Description)) -} diff --git a/spine/send.go b/spine/send.go deleted file mode 100644 index 7b89a8fe..00000000 --- a/spine/send.go +++ /dev/null @@ -1,273 +0,0 @@ -package spine - -import ( - "encoding/json" - "errors" - "sync/atomic" - - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/ship" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - lru "github.com/hashicorp/golang-lru/v2" -) - -type SenderImpl struct { - msgNum uint64 // 64bit values need to be defined on top of the struct to make atomic commands work on 32bit systems - - // we cache the last 100 notify messages, so we can find the matching item for result errors being returned - datagramNotifyCache *lru.Cache[model.MsgCounterType, model.DatagramType] - - writeHandler ship.SpineDataConnection -} - -var _ Sender = (*SenderImpl)(nil) - -func NewSender(writeI ship.SpineDataConnection) Sender { - cache, _ := lru.New[model.MsgCounterType, model.DatagramType](100) - return &SenderImpl{ - datagramNotifyCache: cache, - writeHandler: writeI, - } -} - -// return the datagram for a given msgCounter (only availbe for Notify messasges!), error if not found -func (c *SenderImpl) DatagramForMsgCounter(msgCounter model.MsgCounterType) (model.DatagramType, error) { - if datagram, ok := c.datagramNotifyCache.Get(msgCounter); ok { - return datagram, nil - } - - return model.DatagramType{}, errors.New("msgCounter not found") -} - -func (c *SenderImpl) sendSpineMessage(datagram model.DatagramType) error { - // pack into datagram - data := model.Datagram{ - Datagram: datagram, - } - - // marshal - msg, err := json.Marshal(data) - if err != nil { - return err - } - - if c.writeHandler == nil { - return errors.New("outgoing interface implementation not set") - } - - if msg == nil { - return errors.New("message is nil") - } - - logging.Log().Debug(datagram.PrintMessageOverview(true, "", "")) - - // write to channel - c.writeHandler.WriteSpineMessage(msg) - - return nil -} - -// Sends request -func (c *SenderImpl) Request(cmdClassifier model.CmdClassifierType, senderAddress, destinationAddress *model.FeatureAddressType, ackRequest bool, cmd []model.CmdType) (*model.MsgCounterType, error) { - msgCounter := c.getMsgCounter() - - datagram := model.DatagramType{ - Header: model.HeaderType{ - SpecificationVersion: &SpecificationVersion, - AddressSource: senderAddress, - AddressDestination: destinationAddress, - MsgCounter: msgCounter, - CmdClassifier: &cmdClassifier, - }, - Payload: model.PayloadType{ - Cmd: cmd, - }, - } - - if ackRequest { - datagram.Header.AckRequest = &ackRequest - } - - return msgCounter, c.sendSpineMessage(datagram) -} - -func (c *SenderImpl) ResultSuccess(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType) error { - return c.result(requestHeader, senderAddress, nil) -} - -func (c *SenderImpl) ResultError(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error { - return c.result(requestHeader, senderAddress, err) -} - -// sends a result for a request -func (c *SenderImpl) result(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, err *model.ErrorType) error { - cmdClassifier := model.CmdClassifierTypeResult - - addressSource := *requestHeader.AddressDestination - addressSource.Device = senderAddress.Device - - var resultData model.ResultDataType - if err != nil { - resultData = model.ResultDataType{ - ErrorNumber: &err.ErrorNumber, - Description: err.Description, - } - } else { - resultData = model.ResultDataType{ - ErrorNumber: util.Ptr(model.ErrorNumberTypeNoError), - } - } - - cmd := model.CmdType{ - ResultData: &resultData, - } - - datagram := model.DatagramType{ - Header: model.HeaderType{ - SpecificationVersion: &SpecificationVersion, - AddressSource: &addressSource, - AddressDestination: requestHeader.AddressSource, - MsgCounter: c.getMsgCounter(), - MsgCounterReference: requestHeader.MsgCounter, - CmdClassifier: &cmdClassifier, - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{cmd}, - }, - } - - return c.sendSpineMessage(datagram) -} - -// Reply sends reply to original sender -func (c *SenderImpl) Reply(requestHeader *model.HeaderType, senderAddress *model.FeatureAddressType, cmd model.CmdType) error { - cmdClassifier := model.CmdClassifierTypeReply - - addressSource := *requestHeader.AddressDestination - addressSource.Device = senderAddress.Device - - datagram := model.DatagramType{ - Header: model.HeaderType{ - SpecificationVersion: &SpecificationVersion, - AddressSource: &addressSource, - AddressDestination: requestHeader.AddressSource, - MsgCounter: c.getMsgCounter(), - MsgCounterReference: requestHeader.MsgCounter, - CmdClassifier: &cmdClassifier, - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{cmd}, - }, - } - - return c.sendSpineMessage(datagram) -} - -// Notify sends notification to destination -func (c *SenderImpl) Notify(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) { - msgCounter := c.getMsgCounter() - - cmdClassifier := model.CmdClassifierTypeNotify - - datagram := model.DatagramType{ - Header: model.HeaderType{ - SpecificationVersion: &SpecificationVersion, - AddressSource: senderAddress, - AddressDestination: destinationAddress, - MsgCounter: msgCounter, - CmdClassifier: &cmdClassifier, - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{cmd}, - }, - } - - c.datagramNotifyCache.Add(*msgCounter, datagram) - - return msgCounter, c.sendSpineMessage(datagram) -} - -// Write sends notification to destination -func (c *SenderImpl) Write(senderAddress, destinationAddress *model.FeatureAddressType, cmd model.CmdType) (*model.MsgCounterType, error) { - msgCounter := c.getMsgCounter() - - cmdClassifier := model.CmdClassifierTypeWrite - ackRequest := true - - datagram := model.DatagramType{ - Header: model.HeaderType{ - SpecificationVersion: &SpecificationVersion, - AddressSource: senderAddress, - AddressDestination: destinationAddress, - MsgCounter: msgCounter, - CmdClassifier: &cmdClassifier, - AckRequest: &ackRequest, - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{cmd}, - }, - } - - return msgCounter, c.sendSpineMessage(datagram) -} - -// Send a subscription request to a remote server feature -func (c *SenderImpl) Subscribe(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) { - - cmd := model.CmdType{ - NodeManagementSubscriptionRequestCall: NewNodeManagementSubscriptionRequestCallType(senderAddress, destinationAddress, serverFeatureType), - } - - // we always send it to the remote NodeManagment feature, which always is at entity:[0],feature:0 - localAddress := NodeManagementAddress(senderAddress.Device) - remoteAddress := NodeManagementAddress(destinationAddress.Device) - - return c.Request(model.CmdClassifierTypeCall, localAddress, remoteAddress, true, []model.CmdType{cmd}) -} - -// Send a subscription deletion request to a remote server feature -func (c *SenderImpl) Unsubscribe(senderAddress, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) { - - cmd := model.CmdType{ - NodeManagementSubscriptionDeleteCall: NewNodeManagementSubscriptionDeleteCallType(senderAddress, destinationAddress), - } - - // we always send it to the remote NodeManagment feature, which always is at entity:[0],feature:0 - localAddress := NodeManagementAddress(senderAddress.Device) - remoteAddress := NodeManagementAddress(destinationAddress.Device) - - return c.Request(model.CmdClassifierTypeCall, localAddress, remoteAddress, true, []model.CmdType{cmd}) -} - -// Send a binding request to a remote server feature -func (c *SenderImpl) Bind(senderAddress, destinationAddress *model.FeatureAddressType, serverFeatureType model.FeatureTypeType) (*model.MsgCounterType, error) { - cmd := model.CmdType{ - NodeManagementBindingRequestCall: NewNodeManagementBindingRequestCallType(senderAddress, destinationAddress, serverFeatureType), - } - - // we always send it to the remote NodeManagment feature, which always is at entity:[0],feature:0 - localAddress := NodeManagementAddress(senderAddress.Device) - remoteAddress := NodeManagementAddress(destinationAddress.Device) - - return c.Request(model.CmdClassifierTypeCall, localAddress, remoteAddress, true, []model.CmdType{cmd}) -} - -// Send a binding request to a remote server feature -func (c *SenderImpl) Unbind(senderAddress, destinationAddress *model.FeatureAddressType) (*model.MsgCounterType, error) { - cmd := model.CmdType{ - NodeManagementBindingDeleteCall: NewNodeManagementBindingDeleteCallType(senderAddress, destinationAddress), - } - - // we always send it to the remote NodeManagment feature, which always is at entity:[0],feature:0 - localAddress := NodeManagementAddress(senderAddress.Device) - remoteAddress := NodeManagementAddress(destinationAddress.Device) - - return c.Request(model.CmdClassifierTypeCall, localAddress, remoteAddress, true, []model.CmdType{cmd}) -} - -func (c *SenderImpl) getMsgCounter() *model.MsgCounterType { - // TODO: persistence - i := model.MsgCounterType(atomic.AddUint64(&c.msgNum, 1)) - return &i -} diff --git a/spine/send_test.go b/spine/send_test.go deleted file mode 100644 index 0219c774..00000000 --- a/spine/send_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package spine - -import ( - "encoding/json" - "testing" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" -) - -func TestSender_Reply_MsgCounter(t *testing.T) { - temp := &WriteMessageHandler{} - sut := NewSender(temp) - - senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) - requestHeader := &model.HeaderType{ - AddressSource: senderAddress, - AddressDestination: destinationAddress, - MsgCounter: util.Ptr(model.MsgCounterType(10)), - } - cmd := model.CmdType{ - ResultData: &model.ResultDataType{ErrorNumber: util.Ptr(model.ErrorNumberType(model.ErrorNumberTypeNoError))}, - } - - err := sut.Reply(requestHeader, senderAddress, cmd) - assert.NoError(t, err) - - // Act - err = sut.Reply(requestHeader, senderAddress, cmd) - assert.NoError(t, err) - expectedMsgCounter := 2 //because Notify was called twice - - sentBytes := temp.LastMessage() - var sentDatagram model.Datagram - assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) - assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) -} - -func TestSender_Notify_MsgCounter(t *testing.T) { - temp := &WriteMessageHandler{} - sut := NewSender(temp) - - senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) - cmd := model.CmdType{ - ResultData: &model.ResultDataType{ErrorNumber: util.Ptr(model.ErrorNumberType(model.ErrorNumberTypeNoError))}, - } - - _, err := sut.Notify(senderAddress, destinationAddress, cmd) - assert.NoError(t, err) - - // Act - _, err = sut.Notify(senderAddress, destinationAddress, cmd) - assert.NoError(t, err) - expectedMsgCounter := 2 //because Notify was called twice - - sentBytes := temp.LastMessage() - var sentDatagram model.Datagram - assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) - assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) - - _, err = sut.DatagramForMsgCounter(model.MsgCounterType(2)) - assert.NoError(t, err) - - _, err = sut.DatagramForMsgCounter(model.MsgCounterType(3)) - assert.Error(t, err) -} - -func TestSender_Write_MsgCounter(t *testing.T) { - temp := &WriteMessageHandler{} - sut := NewSender(temp) - - senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) - cmd := model.CmdType{ - ResultData: &model.ResultDataType{ErrorNumber: util.Ptr(model.ErrorNumberType(model.ErrorNumberTypeNoError))}, - } - - _, err := sut.Write(senderAddress, destinationAddress, cmd) - assert.NoError(t, err) - - // Act - _, err = sut.Write(senderAddress, destinationAddress, cmd) - assert.NoError(t, err) - expectedMsgCounter := 2 //because Write was called twice - - sentBytes := temp.LastMessage() - var sentDatagram model.Datagram - assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) - assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) -} - -func TestSender_Subscribe_MsgCounter(t *testing.T) { - temp := &WriteMessageHandler{} - sut := NewSender(temp) - - senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) - - _, err := sut.Subscribe(senderAddress, destinationAddress, model.FeatureTypeTypeLoadControl) - assert.NoError(t, err) - - // Act - _, err = sut.Subscribe(senderAddress, destinationAddress, model.FeatureTypeTypeLoadControl) - assert.NoError(t, err) - expectedMsgCounter := 2 //because Write was called twice - - sentBytes := temp.LastMessage() - var sentDatagram model.Datagram - assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) - assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) -} - -func TestSender_Unsubscribe_MsgCounter(t *testing.T) { - temp := &WriteMessageHandler{} - sut := NewSender(temp) - - senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) - - _, err := sut.Unsubscribe(senderAddress, destinationAddress) - assert.NoError(t, err) - - // Act - _, err = sut.Unsubscribe(senderAddress, destinationAddress) - assert.NoError(t, err) - expectedMsgCounter := 2 //because Write was called twice - - sentBytes := temp.LastMessage() - var sentDatagram model.Datagram - assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) - assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) -} - -func TestSender_Bind_MsgCounter(t *testing.T) { - temp := &WriteMessageHandler{} - sut := NewSender(temp) - - senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) - - _, err := sut.Bind(senderAddress, destinationAddress, model.FeatureTypeTypeLoadControl) - assert.NoError(t, err) - - // Act - _, err = sut.Bind(senderAddress, destinationAddress, model.FeatureTypeTypeLoadControl) - assert.NoError(t, err) - expectedMsgCounter := 2 //because Write was called twice - - sentBytes := temp.LastMessage() - var sentDatagram model.Datagram - assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) - assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) -} - -func TestSender_Unbind_MsgCounter(t *testing.T) { - temp := &WriteMessageHandler{} - sut := NewSender(temp) - - senderAddress := featureAddressType(1, NewEntityAddressType("Sender", []uint{1})) - destinationAddress := featureAddressType(2, NewEntityAddressType("destination", []uint{1})) - - _, err := sut.Unbind(senderAddress, destinationAddress) - assert.NoError(t, err) - - // Act - _, err = sut.Unbind(senderAddress, destinationAddress) - assert.NoError(t, err) - expectedMsgCounter := 2 //because Write was called twice - - sentBytes := temp.LastMessage() - var sentDatagram model.Datagram - assert.NoError(t, json.Unmarshal(sentBytes, &sentDatagram)) - assert.Equal(t, expectedMsgCounter, int(*sentDatagram.Datagram.Header.MsgCounter)) -} diff --git a/spine/subscription_manager.go b/spine/subscription_manager.go deleted file mode 100644 index 093b4c64..00000000 --- a/spine/subscription_manager.go +++ /dev/null @@ -1,227 +0,0 @@ -package spine - -import ( - "errors" - "fmt" - "reflect" - "sync" - "sync/atomic" - - "github.com/ahmetb/go-linq/v3" - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" -) - -type SubscriptionEntry struct { - id uint64 - serverFeature FeatureLocal - clientFeature FeatureRemote -} - -type SubscriptionManagerImpl struct { - localDevice DeviceLocal - - subscriptionNum uint64 - subscriptionEntries []*SubscriptionEntry - - mux sync.Mutex - // TODO: add persistence -} - -func NewSubscriptionManager(localDevice DeviceLocal) *SubscriptionManagerImpl { - c := &SubscriptionManagerImpl{ - subscriptionNum: 0, - localDevice: localDevice, - } - - return c -} - -// is sent from the client (remote device) to the server (local device) -func (c *SubscriptionManagerImpl) AddSubscription(remoteDevice DeviceRemote, data model.SubscriptionManagementRequestCallType) error { - - serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) - if serverFeature == nil { - return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *c.localDevice.Address()) - } - if err := c.checkRoleAndType(serverFeature, model.RoleTypeServer, *data.ServerFeatureType); err != nil { - return err - } - - clientFeature := remoteDevice.FeatureByAddress(data.ClientAddress) - if clientFeature == nil { - return fmt.Errorf("client feature '%s' in remote device '%s' not found", data.ClientAddress, *remoteDevice.Address()) - } - if err := c.checkRoleAndType(clientFeature, model.RoleTypeClient, *data.ServerFeatureType); err != nil { - return err - } - - subscriptionEntry := &SubscriptionEntry{ - id: c.subscriptionId(), - serverFeature: serverFeature, - clientFeature: clientFeature, - } - - c.mux.Lock() - defer c.mux.Unlock() - - for _, item := range c.subscriptionEntries { - if reflect.DeepEqual(item.serverFeature, serverFeature) && reflect.DeepEqual(item.clientFeature, clientFeature) { - return fmt.Errorf("requested subscription is already present") - } - } - - c.subscriptionEntries = append(c.subscriptionEntries, subscriptionEntry) - - payload := EventPayload{ - Ski: remoteDevice.Ski(), - EventType: EventTypeSubscriptionChange, - ChangeType: ElementChangeAdd, - Data: data, - Feature: clientFeature, - } - Events.Publish(payload) - - return nil -} - -// Remove a specific subscription that is provided by a delete message from a remote device -func (c *SubscriptionManagerImpl) RemoveSubscription(data model.SubscriptionManagementDeleteCallType, remoteDevice DeviceRemote) error { - var newSubscriptionEntries []*SubscriptionEntry - - // according to the spec 7.4.4 - // a. The absence of "subscriptionDelete. clientAddress. device" SHALL be treated as if it was - // present and set to the sender's "device" address part. - // b. The absence of "subscriptionDelete. serverAddress. device" SHALL be treated as if it was - // present and set to the recipient's "device" address part. - - var clientAddress model.FeatureAddressType - util.DeepCopy(data.ClientAddress, &clientAddress) - if data.ClientAddress.Device == nil { - clientAddress.Device = remoteDevice.Address() - } - - clientFeature := remoteDevice.FeatureByAddress(data.ClientAddress) - if clientFeature == nil { - return fmt.Errorf("client feature '%s' in remote device '%s' not found", data.ClientAddress, *remoteDevice.Address()) - } - - serverFeature := c.localDevice.FeatureByAddress(data.ServerAddress) - if serverFeature == nil { - return fmt.Errorf("server feature '%s' in local device '%s' not found", data.ServerAddress, *c.localDevice.Address()) - } - - c.mux.Lock() - defer c.mux.Unlock() - - for _, item := range c.subscriptionEntries { - itemAddress := item.clientFeature.Address() - - if !reflect.DeepEqual(*itemAddress, clientAddress) && - !reflect.DeepEqual(item.serverFeature, serverFeature) { - newSubscriptionEntries = append(newSubscriptionEntries, item) - } - } - - if len(newSubscriptionEntries) == len(c.subscriptionEntries) { - return errors.New("could not find requested SubscriptionId to be removed") - } - - c.subscriptionEntries = newSubscriptionEntries - - payload := EventPayload{ - Ski: remoteDevice.Ski(), - EventType: EventTypeSubscriptionChange, - ChangeType: ElementChangeRemove, - Data: data, - Device: remoteDevice, - Feature: clientFeature, - } - Events.Publish(payload) - - return nil -} - -// Remove all existing subscriptions for a given remote device -func (c *SubscriptionManagerImpl) RemoveSubscriptionsForDevice(remoteDevice DeviceRemote) { - if remoteDevice == nil { - return - } - - for _, entity := range remoteDevice.Entities() { - c.RemoveSubscriptionsForEntity(entity) - } -} - -// Remove all existing subscriptions for a given remote device entity -func (c *SubscriptionManagerImpl) RemoveSubscriptionsForEntity(remoteEntity EntityRemote) { - if remoteEntity == nil { - return - } - - c.mux.Lock() - defer c.mux.Unlock() - - var newSubscriptionEntries []*SubscriptionEntry - for _, item := range c.subscriptionEntries { - if !reflect.DeepEqual(item.clientFeature.Address().Entity, remoteEntity.Address().Entity) { - newSubscriptionEntries = append(newSubscriptionEntries, item) - continue - } - - clientFeature := remoteEntity.Feature(item.clientFeature.Address().Feature) - payload := EventPayload{ - Ski: remoteEntity.Device().Ski(), - EventType: EventTypeSubscriptionChange, - ChangeType: ElementChangeRemove, - Entity: remoteEntity, - Feature: clientFeature, - } - Events.Publish(payload) - } - - c.subscriptionEntries = newSubscriptionEntries -} - -func (c *SubscriptionManagerImpl) Subscriptions(remoteDevice DeviceRemote) []*SubscriptionEntry { - var result []*SubscriptionEntry - - c.mux.Lock() - defer c.mux.Unlock() - - linq.From(c.subscriptionEntries).WhereT(func(s *SubscriptionEntry) bool { - return s.clientFeature.Device().Ski() == remoteDevice.Ski() - }).ToSlice(&result) - - return result -} - -func (c *SubscriptionManagerImpl) SubscriptionsOnFeature(featureAddress model.FeatureAddressType) []*SubscriptionEntry { - var result []*SubscriptionEntry - - c.mux.Lock() - defer c.mux.Unlock() - - linq.From(c.subscriptionEntries).WhereT(func(s *SubscriptionEntry) bool { - return reflect.DeepEqual(*s.serverFeature.Address(), featureAddress) - }).ToSlice(&result) - - return result -} - -func (c *SubscriptionManagerImpl) subscriptionId() uint64 { - i := atomic.AddUint64(&c.subscriptionNum, 1) - return i -} - -func (c *SubscriptionManagerImpl) checkRoleAndType(feature Feature, role model.RoleType, featureType model.FeatureTypeType) error { - if feature.Role() != model.RoleTypeSpecial && feature.Role() != role { - return fmt.Errorf("found feature %s is not matching required role %s", feature.Type(), role) - } - - if feature.Type() != featureType && feature.Type() != model.FeatureTypeTypeGeneric { - return fmt.Errorf("found feature %s is not matching required type %s", feature.Type(), featureType) - } - - return nil -} diff --git a/spine/subscription_manager_test.go b/spine/subscription_manager_test.go deleted file mode 100644 index 22a57412..00000000 --- a/spine/subscription_manager_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package spine - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestSubscriptionManagerSuite(t *testing.T) { - suite.Run(t, new(SubscriptionManagerSuite)) -} - -type SubscriptionManagerSuite struct { - suite.Suite - - localDevice DeviceLocal - remoteDevice DeviceRemote - sut SubscriptionManager -} - -func (suite *SubscriptionManagerSuite) WriteSpineMessage([]byte) {} - -func (suite *SubscriptionManagerSuite) SetupSuite() { - suite.localDevice = NewDeviceLocalImpl("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - - ski := "test" - sender := NewSender(suite) - suite.remoteDevice = NewDeviceRemoteImpl(suite.localDevice, ski, sender) - - _ = suite.localDevice.SetupRemoteDevice(ski, suite) - - suite.sut = NewSubscriptionManager(suite.localDevice) -} - -func (suite *SubscriptionManagerSuite) Test_Subscriptions() { - entity := NewEntityLocalImpl(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) - suite.localDevice.AddEntity(entity) - - localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - - remoteEntity := NewEntityRemoteImpl(suite.remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{1}) - suite.remoteDevice.AddEntity(remoteEntity) - - remoteFeature := NewFeatureRemoteImpl(remoteEntity.NextFeatureId(), remoteEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - remoteFeature.Address().Device = util.Ptr(model.AddressDeviceType("remoteDevice")) - remoteEntity.AddFeature(remoteFeature) - - subscrRequest := model.SubscriptionManagementRequestCallType{ - ClientAddress: remoteFeature.Address(), - ServerAddress: localFeature.Address(), - ServerFeatureType: util.Ptr(model.FeatureTypeTypeDeviceDiagnosis), - } - - subMgr := suite.localDevice.SubscriptionManager() - err := subMgr.AddSubscription(suite.remoteDevice, subscrRequest) - assert.Nil(suite.T(), err) - - subs := subMgr.Subscriptions(suite.remoteDevice) - assert.Equal(suite.T(), 1, len(subs)) - - err = subMgr.AddSubscription(suite.remoteDevice, subscrRequest) - assert.NotNil(suite.T(), err) - - subs = subMgr.Subscriptions(suite.remoteDevice) - assert.Equal(suite.T(), 1, len(subs)) - - subscrDelete := model.SubscriptionManagementDeleteCallType{ - ClientAddress: remoteFeature.Address(), - ServerAddress: localFeature.Address(), - } - - err = subMgr.RemoveSubscription(subscrDelete, suite.remoteDevice) - assert.Nil(suite.T(), err) - - subs = subMgr.Subscriptions(suite.remoteDevice) - assert.Equal(suite.T(), 0, len(subs)) - - err = subMgr.RemoveSubscription(subscrDelete, suite.remoteDevice) - assert.NotNil(suite.T(), err) - - subMgr = suite.localDevice.SubscriptionManager() - err = subMgr.AddSubscription(suite.remoteDevice, subscrRequest) - assert.Nil(suite.T(), err) - - subs = subMgr.Subscriptions(suite.remoteDevice) - assert.Equal(suite.T(), 1, len(subs)) - - subMgr.RemoveSubscriptionsForDevice(suite.remoteDevice) - - subs = subMgr.Subscriptions(suite.remoteDevice) - assert.Equal(suite.T(), 0, len(subs)) -} diff --git a/spine/testdata/nm_destinationListData_recv_read.json b/spine/testdata/nm_destinationListData_recv_read.json deleted file mode 100644 index 9c4c1f65..00000000 --- a/spine/testdata/nm_destinationListData_recv_read.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.2.0", - "addressSource": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 1, - "cmdClassifier": "read" - }, - "payload": { - "cmd": [ - { - "nodeManagementDestinationListData": {} - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_destinationListData_send_reply_expected.json b/spine/testdata/nm_destinationListData_send_reply_expected.json deleted file mode 100644 index f5c89a46..00000000 --- a/spine/testdata/nm_destinationListData_send_reply_expected.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.3.0", - "addressSource": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 2, - "msgCounterReference": 1, - "cmdClassifier": "reply" - }, - "payload": { - "cmd": [ - { - "nodeManagementDestinationListData": { - "nodeManagementDestinationData": [ - { - "deviceDescription": { - "deviceAddress": { - "device": "TestDeviceAddress" - }, - "deviceType": "EnergyManagementSystem", - "networkFeatureSet": "smart" - } - } - ] - } - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_detaileddiscoverydata_recv_read.json b/spine/testdata/nm_detaileddiscoverydata_recv_read.json deleted file mode 100644 index 536b5d3a..00000000 --- a/spine/testdata/nm_detaileddiscoverydata_recv_read.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.2.0", - "addressSource": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 1, - "cmdClassifier": "read" - }, - "payload": { - "cmd": [ - { - "nodeManagementDetailedDiscoveryData": {} - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_detaileddiscoverydata_recv_read_ack.json b/spine/testdata/nm_detaileddiscoverydata_recv_read_ack.json deleted file mode 100644 index fc169543..00000000 --- a/spine/testdata/nm_detaileddiscoverydata_recv_read_ack.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.2.0", - "addressSource": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 1, - "cmdClassifier": "read", - "ackRequest":true - }, - "payload": { - "cmd": [ - { - "nodeManagementDetailedDiscoveryData": {} - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_detaileddiscoverydata_send_read_expected.json b/spine/testdata/nm_detaileddiscoverydata_send_read_expected.json deleted file mode 100644 index 02d814c7..00000000 --- a/spine/testdata/nm_detaileddiscoverydata_send_read_expected.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.3.0", - "addressSource": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 1, - "cmdClassifier": "read" - }, - "payload": { - "cmd": [ - { - "nodeManagementDetailedDiscoveryData": {} - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_detaileddiscoverydata_send_reply_expected.json b/spine/testdata/nm_detaileddiscoverydata_send_reply_expected.json deleted file mode 100644 index bc363225..00000000 --- a/spine/testdata/nm_detaileddiscoverydata_send_reply_expected.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.3.0", - "addressSource": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 2, - "msgCounterReference": 1, - "cmdClassifier": "reply" - }, - "payload": { - "cmd": [ - { - "nodeManagementDetailedDiscoveryData": { - "specificationVersionList": { - "specificationVersion": [ - "1.3.0" - ] - }, - "deviceInformation": { - "description": { - "deviceAddress": { - "device": "TestDeviceAddress" - }, - "deviceType": "EnergyManagementSystem", - "networkFeatureSet": "smart" - } - }, - "entityInformation": [ - { - "description": { - "entityAddress": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ] - }, - "entityType": "DeviceInformation" - } - } - ], - "featureInformation": [ - { - "description": { - "featureAddress": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 0 - }, - "featureType": "NodeManagement", - "role": "special", - "supportedFunction": [ - { - "function": "nodeManagementSubscriptionDeleteCall", - "possibleOperations": {} - }, - { - "function": "nodeManagementBindingData", - "possibleOperations": { - "read": {} - } - }, - { - "function": "nodeManagementBindingRequestCall", - "possibleOperations": {} - }, - { - "function": "nodeManagementBindingDeleteCall", - "possibleOperations": {} - }, - { - "function": "nodeManagementDestinationListData", - "possibleOperations": { - "read": {} - } - }, - { - "function": "nodeManagementDetailedDiscoveryData", - "possibleOperations": { - "read": {} - } - }, - { - "function": "nodeManagementUseCaseData", - "possibleOperations": { - "read": {} - } - }, - { - "function": "nodeManagementSubscriptionData", - "possibleOperations": { - "read": {} - } - }, - { - "function": "nodeManagementSubscriptionRequestCall", - "possibleOperations": {} - } - ] - } - }, - { - "description": { - "featureAddress": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 1 - }, - "featureType": "DeviceClassification", - "role": "server", - "supportedFunction": [ - { - "function": "deviceClassificationManufacturerData", - "possibleOperations": { - "read": {} - } - } - ] - } - } - ] - } - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_detaileddiscoverydata_send_result_expected.json b/spine/testdata/nm_detaileddiscoverydata_send_result_expected.json deleted file mode 100644 index 33848398..00000000 --- a/spine/testdata/nm_detaileddiscoverydata_send_result_expected.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.3.0", - "addressSource": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 3, - "msgCounterReference": 1, - "cmdClassifier": "result" - }, - "payload": { - "cmd": [ - { - "resultData": { - "errorNumber": 0 - } - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_subscriptionRequestCall_recv_call.json b/spine/testdata/nm_subscriptionRequestCall_recv_call.json deleted file mode 100644 index 3061f941..00000000 --- a/spine/testdata/nm_subscriptionRequestCall_recv_call.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.2.0", - "addressSource": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 1, - "cmdClassifier": "call", - "ackRequest": true - }, - "payload": { - "cmd": [ - { - "nodeManagementSubscriptionRequestCall": { - "subscriptionRequest": { - "clientAddress": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "serverAddress": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 0 - }, - "serverFeatureType": "NodeManagement" - } - } - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_subscriptionRequestCall_send_result_expected.json b/spine/testdata/nm_subscriptionRequestCall_send_result_expected.json deleted file mode 100644 index 9c369b67..00000000 --- a/spine/testdata/nm_subscriptionRequestCall_send_result_expected.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.3.0", - "addressSource": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 2, - "msgCounterReference": 1, - "cmdClassifier": "result" - }, - "payload": { - "cmd": [ - { - "resultData": { - "errorNumber": 0 - } - } - ] - } - } -} \ No newline at end of file diff --git a/spine/testdata/nm_usecaseinformationlistdata_recv_reply.json b/spine/testdata/nm_usecaseinformationlistdata_recv_reply.json deleted file mode 100644 index 14d86965..00000000 --- a/spine/testdata/nm_usecaseinformationlistdata_recv_reply.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "datagram": { - "header": { - "specificationVersion": "1.3.0", - "addressSource": { - "device": "TestDeviceAddress", - "entity": [ - 0 - ], - "feature": 0 - }, - "addressDestination": { - "device": "HEMS", - "entity": [ - 0 - ], - "feature": 0 - }, - "msgCounter": 2, - "msgCounterReference": 1, - "cmdClassifier": "reply" - }, - "payload": { - "cmd": [ - { - "nodeManagementUseCaseData": { - "useCaseInformation": [ - { - "address": { - "device": "d:_i:47859_Elli-Wallbox-xxxxxxxxxx" - }, - "actor": "EVSE", - "useCaseSupport": [ - { - "useCaseName": "evseCommissioningAndConfiguration", - "useCaseVersion": "1.0.1", - "scenarioSupport": [ - 1, - 2 - ] - }, - { - "useCaseName": "evChargingSummary", - "useCaseVersion": "1.0.1", - "scenarioSupport": [ - 1 - ] - } - ] - }, - { - "address": { - "device": "d:_i:47859_Elli-Wallbox-2041A0ZW5R" - }, - "actor": "EV", - "useCaseSupport": [ - { - "useCaseName": "evCommissioningAndConfiguration", - "useCaseVersion": "1.0.1", - "scenarioSupport": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ] - }, - { - "useCaseName": "overloadProtectionByEvChargingCurrentCurtailment", - "useCaseVersion": "1.0.1", - "scenarioSupport": [ - 1, - 2, - 3 - ] - }, - { - "useCaseName": "coordinatedEvCharging", - "useCaseVersion": "1.0.1", - "scenarioSupport": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ] - }, - { - "useCaseName": "measurementOfElectricityDuringEvCharging", - "useCaseVersion": "1.0.1", - "scenarioSupport": [ - 1, - 2, - 3 - ] - }, - { - "useCaseName": "optimizationOfSelfConsumptionDuringEvCharging", - "useCaseVersion": "1.0.1", - "scenarioSupport": [ - 1, - 2, - 3 - ] - } - ] - } - ] - } - } - ] - } - } -} \ No newline at end of file diff --git a/util/channel.go b/util/channel.go index 5419193f..98a226c8 100644 --- a/util/channel.go +++ b/util/channel.go @@ -1,18 +1,5 @@ package util -import "time" - -func ReceiveWithTimeout[T any](c chan T, maxDelay time.Duration) T { - timeout := time.After(maxDelay) - - select { - case data := <-c: - return data - case <-timeout: - return Zero[T]() - } -} - // check if a provided channel is closed func IsChannelClosed[T any](ch <-chan T) bool { select { diff --git a/util/type.go b/util/type.go deleted file mode 100644 index 44478bf3..00000000 --- a/util/type.go +++ /dev/null @@ -1,13 +0,0 @@ -package util - -import "reflect" - -func Type[T any]() reflect.Type { - return reflect.TypeOf((*T)(nil)).Elem() -} - -// checks if type T implements interface I -func Implements[T any, I any]() bool { - _, supported := any((*T)(nil)).(I) - return supported -} diff --git a/util/zero.go b/util/zero.go deleted file mode 100644 index a74a149a..00000000 --- a/util/zero.go +++ /dev/null @@ -1,15 +0,0 @@ -package util - -import "reflect" - -func Zero[T any]() (ret T) { - return -} - -func IsZero[T comparable](v T) bool { - return v == *new(T) -} - -func IsNil[T any](v T) bool { - return !reflect.ValueOf(v).IsValid() -} From 81e0dd83112d9a61f4969dd36466046b95696cd0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Jan 2024 09:03:29 +0100 Subject: [PATCH 155/240] Uncomment mockery generation --- api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api.go b/api/api.go index 856d7030..d924e678 100644 --- a/api/api.go +++ b/api/api.go @@ -6,7 +6,7 @@ import ( spineapi "github.com/enbility/spine-go/api" ) -// //go:generate mockery +//go:generate mockery //go:generate mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider,MdnsService /* EEBUSService */ From 371578dd336f661e954e13f1223c51899a4a2145 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Jan 2024 09:03:44 +0100 Subject: [PATCH 156/240] Move MdnsProvider to ship-go --- go.mod | 6 +- go.sum | 4 +- mdns/.mockery.yaml | 9 --- mdns/api.go | 13 ---- mdns/avahi.go | 183 -------------------------------------------- mdns/helper.go | 20 ----- mdns/types.go | 4 - mdns/zeroconf.go | 103 ------------------------- service/hub.go | 7 +- service/hub_test.go | 6 +- service/mdns.go | 5 +- 11 files changed, 14 insertions(+), 346 deletions(-) delete mode 100644 mdns/.mockery.yaml delete mode 100644 mdns/api.go delete mode 100644 mdns/avahi.go delete mode 100644 mdns/helper.go delete mode 100644 mdns/types.go delete mode 100644 mdns/zeroconf.go diff --git a/go.mod b/go.mod index c01fe5ae..e9b32196 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,8 @@ module github.com/enbility/eebus-go go 1.18 require ( - github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df - github.com/enbility/ship-go v0.0.0-20240114193748-be8afeda7c96 + github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6 github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c - github.com/godbus/dbus/v5 v5.1.0 github.com/gorilla/websocket v1.5.1 github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed github.com/stretchr/testify v1.8.4 @@ -14,8 +12,10 @@ require ( ) require ( + github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df // indirect github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/miekg/dns v1.1.57 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index 4be9e6b3..c226784a 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240114193748-be8afeda7c96 h1:76/ktojmXVnc58qRo93qaDwKhvxCDpvsdE3Mz9fUgv4= -github.com/enbility/ship-go v0.0.0-20240114193748-be8afeda7c96/go.mod h1:OOo/76TU3B9zhMrb0wlftKeVAvICrTTuJpM7xiRT20k= +github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6 h1:QyiMPmuUTL5TCe2Ax4anLtzEn7gvsP/hPjEwfpMqmWM= +github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6/go.mod h1:ZlzDQ8pDzhxT9r+6XMmU50fzgyuFnj9RyWWugJRB8s0= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/mdns/.mockery.yaml b/mdns/.mockery.yaml deleted file mode 100644 index e6c55316..00000000 --- a/mdns/.mockery.yaml +++ /dev/null @@ -1,9 +0,0 @@ -with-expecter: True -inpackage: false -dir: ../mocks/{{ replaceAll .InterfaceDirRelative "internal" "internal_" }} -mockname: "{{.InterfaceName}}" -outpkg: "mocks" -filename: "{{.InterfaceName}}.go" -all: True -packages: - github.com/enbility/eebus-go/mdns: diff --git a/mdns/api.go b/mdns/api.go deleted file mode 100644 index f514900f..00000000 --- a/mdns/api.go +++ /dev/null @@ -1,13 +0,0 @@ -package mdns - -import "net" - -//go:generate mockery - -type MdnsProvider interface { - CheckAvailability() bool - Shutdown() - Announce(serviceName string, port int, txt []string) error - Unannounce() - ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) -} diff --git a/mdns/avahi.go b/mdns/avahi.go deleted file mode 100644 index 8ca50112..00000000 --- a/mdns/avahi.go +++ /dev/null @@ -1,183 +0,0 @@ -package mdns - -import ( - "net" - - "github.com/enbility/ship-go/logging" - "github.com/godbus/dbus/v5" - "github.com/holoplot/go-avahi" -) - -type AvahiProvider struct { - ifaceIndexes []int32 - - avServer *avahi.Server - avEntryGroup *avahi.EntryGroup -} - -func NewAvahiProvider(ifaceIndexes []int32) *AvahiProvider { - return &AvahiProvider{ - ifaceIndexes: ifaceIndexes, - } -} - -var _ MdnsProvider = (*AvahiProvider)(nil) - -func (a *AvahiProvider) CheckAvailability() bool { - dbusConn, err := dbus.SystemBus() - if err != nil { - return false - } - - a.avServer, err = avahi.ServerNew(dbusConn) - if err != nil { - return false - } - - if _, err := a.avServer.GetAPIVersion(); err != nil { - return false - } - - avBrowser, err := a.avServer.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0) - if err != nil { - return false - } - - if avBrowser != nil { - a.avServer.ServiceBrowserFree(avBrowser) - return true - } - - return false -} - -func (a *AvahiProvider) Shutdown() { - if a.avServer == nil { - return - } - - a.avServer.Close() - a.avServer = nil - a.avEntryGroup = nil -} - -func (a *AvahiProvider) Announce(serviceName string, port int, txt []string) error { - logging.Log().Debug("mdns: using avahi") - - entryGroup, err := a.avServer.EntryGroupNew() - if err != nil { - return err - } - - var btxt [][]byte - for _, t := range txt { - btxt = append(btxt, []byte(t)) - } - - for _, iface := range a.ifaceIndexes { - err = entryGroup.AddService(iface, avahi.ProtoUnspec, 0, serviceName, shipZeroConfServiceType, shipZeroConfDomain, "", uint16(port), btxt) - if err != nil { - return err - } - } - - err = entryGroup.Commit() - if err != nil { - return err - } - - a.avEntryGroup = entryGroup - - return nil -} - -func (a *AvahiProvider) Unannounce() { - if a.avEntryGroup == nil { - return - } - - a.avServer.EntryGroupFree(a.avEntryGroup) - a.avEntryGroup = nil -} - -func (a *AvahiProvider) ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) { - var err error - var end bool - - var avBrowser *avahi.ServiceBrowser - - // instead of limiting search on specific allowed interfaces, we allow all and filter the results - if avBrowser, err = a.avServer.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoUnspec, shipZeroConfServiceType, shipZeroConfDomain, 0); err != nil { - logging.Log().Debug("mdns: error setting up avahi browser:", err) - return - } - - if avBrowser == nil { - logging.Log().Debug("mdns: avahi browser is not available") - return - } - - for !end { - select { - case <-cancelChan: - end = true - break - case service := <-avBrowser.AddChannel: - a.processService(service, false, callback) - case service := <-avBrowser.RemoveChannel: - a.processService(service, true, callback) - } - } - - a.avServer.ServiceBrowserFree(avBrowser) -} - -// process an avahi mDNS service -// as avahi returns a service per interface, we need to combine them -func (a *AvahiProvider) processService(service avahi.Service, remove bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) { - // check if the service is within the allowed list - allow := false - if len(a.ifaceIndexes) == 1 && a.ifaceIndexes[0] == avahi.InterfaceUnspec { - allow = true - } else { - for _, iface := range a.ifaceIndexes { - if service.Interface == iface { - allow = true - break - } - } - } - - if !allow { - logging.Log().Debug("avahi - ignoring service as its interface is not in the allowed list:", service.Name) - return - } - - resolved, err := a.avServer.ResolveService(service.Interface, service.Protocol, service.Name, service.Type, service.Domain, avahi.ProtoUnspec, 0) - if err != nil { - logging.Log().Debug("avahi - error resolving service:", service, "error:", err) - return - } - - // convert [][]byte to []string manually - var txt []string - for _, element := range resolved.Txt { - txt = append(txt, string(element)) - } - elements := parseTxt(txt) - - // convert address to net.IP - address := net.ParseIP(resolved.Address) - // if the address can not be used, ignore the entry - if address == nil || address.IsUnspecified() { - logging.Log().Debug("avahi - service provides unusable address:", service.Name) - return - } - - // Ignore IPv6 addresses for now - if address.To4() == nil { - return - } - - callback(elements, resolved.Name, resolved.Host, []net.IP{address}, int(resolved.Port), remove) -} diff --git a/mdns/helper.go b/mdns/helper.go deleted file mode 100644 index c2744288..00000000 --- a/mdns/helper.go +++ /dev/null @@ -1,20 +0,0 @@ -package mdns - -import ( - "strings" -) - -// parse mDNS text fields -func parseTxt(txt []string) map[string]string { - result := make(map[string]string) - - for _, item := range txt { - s := strings.Split(item, "=") - if len(s) != 2 { - continue - } - result[s[0]] = s[1] - } - - return result -} diff --git a/mdns/types.go b/mdns/types.go deleted file mode 100644 index b331744c..00000000 --- a/mdns/types.go +++ /dev/null @@ -1,4 +0,0 @@ -package mdns - -const shipZeroConfServiceType = "_ship._tcp" -const shipZeroConfDomain = "local." diff --git a/mdns/zeroconf.go b/mdns/zeroconf.go deleted file mode 100644 index 3a077255..00000000 --- a/mdns/zeroconf.go +++ /dev/null @@ -1,103 +0,0 @@ -package mdns - -import ( - "context" - "net" - - "github.com/DerAndereAndi/zeroconf/v2" - "github.com/enbility/ship-go/logging" -) - -type ZeroconfProvider struct { - ifaces []net.Interface - - zc *zeroconf.Server -} - -func NewZeroconfProvider(ifaces []net.Interface) *ZeroconfProvider { - return &ZeroconfProvider{ - ifaces: ifaces, - } -} - -var _ MdnsProvider = (*ZeroconfProvider)(nil) - -func (z *ZeroconfProvider) CheckAvailability() bool { - return true -} - -func (z *ZeroconfProvider) Shutdown() {} - -func (z *ZeroconfProvider) Announce(serviceName string, port int, txt []string) error { - logging.Log().Debug("mdns: using zeroconf") - - // use Zeroconf library if avahi is not available - // Set TTL to 2 minutes as defined in SHIP chapter 7 - mDNSServer, err := zeroconf.Register(serviceName, shipZeroConfServiceType, shipZeroConfDomain, port, txt, z.ifaces, zeroconf.TTL(120)) - if err != nil { - return err - } - - z.zc = mDNSServer - - return nil -} - -func (z *ZeroconfProvider) Unannounce() { - if z.zc == nil { - return - } - - z.zc.Shutdown() - z.zc = nil -} - -func (z *ZeroconfProvider) ResolveEntries(cancelChan chan bool, callback func(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool)) { - var end bool - - zcEntries := make(chan *zeroconf.ServiceEntry) - zcRemoved := make(chan *zeroconf.ServiceEntry) - defer close(zcEntries) - defer close(zcRemoved) - - // for Zeroconf we need a context - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - go func() { - _ = zeroconf.Browse(ctx, shipZeroConfServiceType, shipZeroConfDomain, zcEntries, zcRemoved) - }() - - for !end { - select { - case <-ctx.Done(): - end = true - break - case <-cancelChan: - ctx.Done() - case service := <-zcRemoved: - // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records - if len(service.Text) == 0 { - continue - } - - elements := parseTxt(service.Text) - - addresses := service.AddrIPv4 - callback(elements, service.Instance, service.HostName, addresses, service.Port, true) - - case service := <-zcEntries: - // Zeroconf has issues with merging mDNS data and sometimes reports incomplete records - if len(service.Text) == 0 { - continue - } - - elements := parseTxt(service.Text) - - addresses := service.AddrIPv4 - // Only use IPv4 for now - // addresses = append(addresses, service.AddrIPv6...) - callback(elements, service.Instance, service.HostName, addresses, service.Port, false) - } - } -} diff --git a/service/hub.go b/service/hub.go index 410cdaeb..2076648f 100644 --- a/service/hub.go +++ b/service/hub.go @@ -27,7 +27,6 @@ import ( "github.com/gorilla/websocket" ) -const shipWebsocketSubProtocol = "ship" // SHIP 10.2: sub protocol is required for websocket connections const shipWebsocketPath = "/ship/" // used for randomizing the connection initiation delay @@ -406,7 +405,7 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { ReadBufferSize: shipws.MaxMessageSize, WriteBufferSize: shipws.MaxMessageSize, CheckOrigin: func(r *http.Request) bool { return true }, - Subprotocols: []string{shipWebsocketSubProtocol}, // SHIP 10.2: Sub protocol "ship" is required + Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, // SHIP 10.2: Sub protocol "ship" is required } conn, err := upgrader.Upgrade(w, r, nil) @@ -416,7 +415,7 @@ func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // check if the client supports the ship sub protocol - if conn.Subprotocol() != shipWebsocketSubProtocol { + if conn.Subprotocol() != shipapi.ShipWebsocketSubProtocol { logging.Log().Debug("client does not support the ship sub protocol") _ = conn.Close() return @@ -482,7 +481,7 @@ func (h *connectionsHubImpl) connectFoundService(remoteService *api.ServiceDetai InsecureSkipVerify: true, CipherSuites: cert.CiperSuites, }, - Subprotocols: []string{shipWebsocketSubProtocol}, + Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, } address := fmt.Sprintf("wss://%s:%s", host, port) diff --git a/service/hub_test.go b/service/hub_test.go index 66d3355f..0f5a473c 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -297,7 +297,7 @@ func (s *HubSuite) Test_ServeHTTP_01() { con.Close() dialer := &websocket.Dialer{ - Subprotocols: []string{shipWebsocketSubProtocol}, + Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, } con, _, err = dialer.Dial(wsURL, nil) assert.Nil(s.T(), err) @@ -329,7 +329,7 @@ func (s *HubSuite) Test_ServeHTTP_02() { InsecureSkipVerify: true, CipherSuites: cert.CiperSuites, }, - Subprotocols: []string{shipWebsocketSubProtocol}, + Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, } con, _, err := dialer.Dial(wsURL, nil) assert.Nil(s.T(), err) @@ -345,7 +345,7 @@ func (s *HubSuite) Test_ServeHTTP_02() { InsecureSkipVerify: true, CipherSuites: cert.CiperSuites, }, - Subprotocols: []string{shipWebsocketSubProtocol}, + Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, } con, _, err = dialer.Dial(wsURL, nil) assert.Nil(s.T(), err) diff --git a/service/mdns.go b/service/mdns.go index 32ce5a37..32ba06d0 100644 --- a/service/mdns.go +++ b/service/mdns.go @@ -10,9 +10,10 @@ import ( "syscall" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/mdns" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/logging" + "github.com/enbility/ship-go/mdns" "github.com/holoplot/go-avahi" ) @@ -31,7 +32,7 @@ type mdnsManager struct { // the registered callback, only connectionsHub is using this searchDelegate api.MdnsSearch - mdnsProvider mdns.MdnsProvider + mdnsProvider shipapi.MdnsProvider mux sync.Mutex entriesMux sync.Mutex From e009d60ba532a7c32fff41f0720f05b89bb3c89d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Jan 2024 11:40:25 +0100 Subject: [PATCH 157/240] Make configuration and local servicedetails accessible --- api/api.go | 2 + mocks/EEBUSService.go | 94 +++++++++++++++++++++++++++++++++++++++++++ service/service.go | 28 ++++++++----- 3 files changed, 114 insertions(+), 10 deletions(-) diff --git a/api/api.go b/api/api.go index d924e678..7fc0c307 100644 --- a/api/api.go +++ b/api/api.go @@ -42,6 +42,8 @@ type EEBUSService interface { Shutdown() SetLogging(logger logging.Logging) + Configuration() *Configuration + LocalService() *ServiceDetails LocalDevice() spineapi.DeviceLocal RemoteServiceForSKI(ski string) *ServiceDetails RegisterRemoteSKI(ski string, enable bool) diff --git a/mocks/EEBUSService.go b/mocks/EEBUSService.go index 436a2584..ef22e9b1 100644 --- a/mocks/EEBUSService.go +++ b/mocks/EEBUSService.go @@ -57,6 +57,53 @@ func (_c *EEBUSService_CancelPairingWithSKI_Call) RunAndReturn(run func(string)) return _c } +// Configuration provides a mock function with given fields: +func (_m *EEBUSService) Configuration() *api.Configuration { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Configuration") + } + + var r0 *api.Configuration + if rf, ok := ret.Get(0).(func() *api.Configuration); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*api.Configuration) + } + } + + return r0 +} + +// EEBUSService_Configuration_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Configuration' +type EEBUSService_Configuration_Call struct { + *mock.Call +} + +// Configuration is a helper method to define mock.On call +func (_e *EEBUSService_Expecter) Configuration() *EEBUSService_Configuration_Call { + return &EEBUSService_Configuration_Call{Call: _e.mock.On("Configuration")} +} + +func (_c *EEBUSService_Configuration_Call) Run(run func()) *EEBUSService_Configuration_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EEBUSService_Configuration_Call) Return(_a0 *api.Configuration) *EEBUSService_Configuration_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EEBUSService_Configuration_Call) RunAndReturn(run func() *api.Configuration) *EEBUSService_Configuration_Call { + _c.Call.Return(run) + return _c +} + // DisconnectSKI provides a mock function with given fields: ski, reason func (_m *EEBUSService) DisconnectSKI(ski string, reason string) { _m.Called(ski, reason) @@ -171,6 +218,53 @@ func (_c *EEBUSService_LocalDevice_Call) RunAndReturn(run func() spine_goapi.Dev return _c } +// LocalService provides a mock function with given fields: +func (_m *EEBUSService) LocalService() *api.ServiceDetails { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LocalService") + } + + var r0 *api.ServiceDetails + if rf, ok := ret.Get(0).(func() *api.ServiceDetails); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*api.ServiceDetails) + } + } + + return r0 +} + +// EEBUSService_LocalService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalService' +type EEBUSService_LocalService_Call struct { + *mock.Call +} + +// LocalService is a helper method to define mock.On call +func (_e *EEBUSService_Expecter) LocalService() *EEBUSService_LocalService_Call { + return &EEBUSService_LocalService_Call{Call: _e.mock.On("LocalService")} +} + +func (_c *EEBUSService_LocalService_Call) Run(run func()) *EEBUSService_LocalService_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EEBUSService_LocalService_Call) Return(_a0 *api.ServiceDetails) *EEBUSService_LocalService_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EEBUSService_LocalService_Call) RunAndReturn(run func() *api.ServiceDetails) *EEBUSService_LocalService_Call { + _c.Call.Return(run) + return _c +} + // PairingDetailForSki provides a mock function with given fields: ski func (_m *EEBUSService) PairingDetailForSki(ski string) *api.ConnectionStateDetail { ret := _m.Called(ski) diff --git a/service/service.go b/service/service.go index 88df8c4b..ec8cbb33 100644 --- a/service/service.go +++ b/service/service.go @@ -17,10 +17,10 @@ import ( // A service is the central element of an EEBUS service // including its websocket server and a zeroconf service. type EEBUSServiceImpl struct { - Configuration *api.Configuration + configuration *api.Configuration // The local service details - LocalService *api.ServiceDetails + localService *api.ServiceDetails // Connection Registrations connectionsHub api.ConnectionsHub @@ -36,7 +36,7 @@ type EEBUSServiceImpl struct { // creates a new EEBUS service func NewEEBUSService(configuration *api.Configuration, serviceHandler api.EEBUSServiceHandler) *EEBUSServiceImpl { return &EEBUSServiceImpl{ - Configuration: configuration, + configuration: configuration, serviceHandler: serviceHandler, } } @@ -116,7 +116,7 @@ func (s *EEBUSServiceImpl) SetLogging(logger logging.Logging) { // Starts the service by initializeing mDNS and the server. func (s *EEBUSServiceImpl) Setup() error { - sd := s.Configuration + sd := s.configuration if len(sd.Certificate().Certificate) == 0 { return errors.New("missing certificate") @@ -140,10 +140,10 @@ func (s *EEBUSServiceImpl) Setup() error { // The originator's unique ID // I assume those two to mean the same. // TODO: clarify - s.LocalService = api.NewServiceDetails(ski) - s.LocalService.ShipID = sd.Identifier() - s.LocalService.DeviceType = sd.DeviceType() - s.LocalService.RegisterAutoAccept = sd.RegisterAutoAccept() + s.localService = api.NewServiceDetails(ski) + s.localService.ShipID = sd.Identifier() + s.localService.DeviceType = sd.DeviceType() + s.localService.RegisterAutoAccept = sd.RegisterAutoAccept() logging.Log().Info("Local SKI: ", ski) @@ -181,10 +181,10 @@ func (s *EEBUSServiceImpl) Setup() error { } // setup mDNS - mdns := newMDNS(s.LocalService.SKI, s.Configuration) + mdns := newMDNS(s.localService.SKI, s.configuration) // Setup connections hub with mDNS and websocket connection handling - s.connectionsHub = newConnectionsHub(s, mdns, s.spineLocalDevice, s.Configuration, s.LocalService) + s.connectionsHub = newConnectionsHub(s, mdns, s.spineLocalDevice, s.configuration, s.localService) return nil } @@ -202,6 +202,14 @@ func (s *EEBUSServiceImpl) Shutdown() { s.connectionsHub.Shutdown() } +func (s *EEBUSServiceImpl) Configuration() *api.Configuration { + return s.configuration +} + +func (s *EEBUSServiceImpl) LocalService() *api.ServiceDetails { + return s.localService +} + func (s *EEBUSServiceImpl) LocalDevice() spineapi.DeviceLocal { return s.spineLocalDevice } From bbc9c5d26c850124aeb2367f65b10c2a575fdfcc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Jan 2024 13:47:28 +0100 Subject: [PATCH 158/240] Updates for cert and mdns being moved to ship-go repo --- api/api.go | 22 +-- api/configuration_test.go | 2 +- api/connectionstate.go | 15 -- cert/cert.go | 91 --------- cert/cert_test.go | 108 ----------- cmd/evse/main.go | 2 +- cmd/hems/main.go | 2 +- go.mod | 4 +- go.sum | 4 +- mocks/MdnsSearch.go | 68 ------- mocks/MdnsService.go | 255 ------------------------- mocks/ServiceProvider.go | 12 +- mocks/mockgen_api.go | 106 +---------- service/hub.go | 22 +-- service/hub_test.go | 16 +- service/mdns.go | 390 -------------------------------------- service/mdns_test.go | 203 -------------------- service/service.go | 10 +- service/service_test.go | 7 +- 19 files changed, 50 insertions(+), 1289 deletions(-) delete mode 100644 cert/cert.go delete mode 100644 cert/cert_test.go delete mode 100644 mocks/MdnsSearch.go delete mode 100644 mocks/MdnsService.go delete mode 100644 service/mdns.go delete mode 100644 service/mdns_test.go diff --git a/api/api.go b/api/api.go index 7fc0c307..9d7b5751 100644 --- a/api/api.go +++ b/api/api.go @@ -3,11 +3,12 @@ package api import ( "github.com/enbility/ship-go/logging" + shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" ) //go:generate mockery -//go:generate mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider,MdnsService +//go:generate mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider /* EEBUSService */ @@ -62,7 +63,7 @@ type EEBUSService interface { // interface for reporting data from connectionsHub to the Service type ServiceProvider interface { // report a newly discovered remote EEBUS service - VisibleMDNSRecordsUpdated(entries []*MdnsEntry) + VisibleMDNSRecordsUpdated(entries []*shipapi.MdnsEntry) // report a connection to a SKI RemoteSKIConnected(ski string) @@ -93,20 +94,3 @@ type ConnectionsHub interface { CancelPairingWithSKI(ski string) DisconnectSKI(ski string, reason string) } - -/* Mdns */ - -// implemented by hubConnection, used by mdns -type MdnsSearch interface { - ReportMdnsEntries(entries map[string]*MdnsEntry) -} - -// implemented by mdns, used by hubConnection -type MdnsService interface { - SetupMdnsService() error - ShutdownMdnsService() - AnnounceMdnsEntry() error - UnannounceMdnsEntry() - RegisterMdnsSearch(cb MdnsSearch) - UnregisterMdnsSearch(cb MdnsSearch) -} diff --git a/api/configuration_test.go b/api/configuration_test.go index 7d1d8cee..9ce8d13c 100644 --- a/api/configuration_test.go +++ b/api/configuration_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/cert" + "github.com/enbility/ship-go/cert" spinemodel "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" diff --git a/api/connectionstate.go b/api/connectionstate.go index 8d5766c6..4421272a 100644 --- a/api/connectionstate.go +++ b/api/connectionstate.go @@ -2,7 +2,6 @@ package api import ( "errors" - "net" "sync" ) @@ -79,17 +78,3 @@ type RemoteService struct { Type string `json:"type"` Model string `json:"model"` } - -type MdnsEntry struct { - Name string - Ski string - Identifier string // mandatory - Path string // mandatory - Register bool // mandatory - Brand string // optional - Type string // optional - Model string // optional - Host string // mandatory - Port int // mandatory - Addresses []net.IP // mandatory -} diff --git a/cert/cert.go b/cert/cert.go deleted file mode 100644 index e08229c1..00000000 --- a/cert/cert.go +++ /dev/null @@ -1,91 +0,0 @@ -package cert - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "errors" - "fmt" - "math/big" - "time" -) - -var CiperSuites = []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, // SHIP 9.1: required cipher suite - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // SHIP 9.1: optional cipher suite -} - -// Create a ship compatible self signed certificate -// organizationalUnit is the OU of the certificate -// organization is the O of the certificate -// country is the C of the certificate -// commonName is the CN of the certificate -// Example for commonName: "deviceModel-deviceSerialNumber" -func CreateCertificate(organizationalUnit, organization, country, commonName string) (tls.Certificate, error) { - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return tls.Certificate{}, err - } - - // Create the EEBUS service SKI using the private key - asn1, err := x509.MarshalECPrivateKey(privateKey) - if err != nil { - return tls.Certificate{}, err - } - // SHIP 12.2: Required to be created according to RFC 3280 4.2.1.2 - ski := sha1.Sum(asn1) - - subject := pkix.Name{ - OrganizationalUnit: []string{organizationalUnit}, - Organization: []string{organization}, - Country: []string{country}, - CommonName: commonName, - } - - // Create a random serial big int value - maxValue := new(big.Int) - maxValue.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxValue, big.NewInt(1)) - serialNumber, err := rand.Int(rand.Reader, maxValue) - if err != nil { - return tls.Certificate{}, err - } - - template := x509.Certificate{ - SignatureAlgorithm: x509.ECDSAWithSHA256, - SerialNumber: serialNumber, - Subject: subject, - NotBefore: time.Now(), // Valid starting now - NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10), // Valid for 10 years - KeyUsage: x509.KeyUsageDigitalSignature, - BasicConstraintsValid: true, - IsCA: true, - SubjectKeyId: ski[:], - } - - certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) - if err != nil { - return tls.Certificate{}, err - } - - tlsCertificate := tls.Certificate{ - Certificate: [][]byte{certBytes}, - PrivateKey: privateKey, - SupportedSignatureAlgorithms: []tls.SignatureScheme{tls.ECDSAWithP256AndSHA256}, - } - - return tlsCertificate, nil -} - -func SkiFromCertificate(cert *x509.Certificate) (string, error) { - // check if the clients certificate provides a SKI - subjectKeyId := cert.SubjectKeyId - if len(subjectKeyId) != 20 { - return "", errors.New("Client certificate does not provide a SKI") - } - - return fmt.Sprintf("%0x", subjectKeyId), nil -} diff --git a/cert/cert_test.go b/cert/cert_test.go deleted file mode 100644 index 73f90d8a..00000000 --- a/cert/cert_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package cert - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "math/big" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestCertSuite(t *testing.T) { - suite.Run(t, new(CertSuite)) -} - -type CertSuite struct { - suite.Suite -} - -func (c *CertSuite) Test_CreateCertificate() { - cert, err := CreateCertificate("", "Org", "DE", "CN") - assert.Nil(c.T(), err) - assert.NotNil(c.T(), cert) -} - -func (c *CertSuite) Test_SkiFromCertificate() { - cert, err := CreateCertificate("", "Org", "DE", "CN") - assert.Nil(c.T(), err) - - leaf, err := x509.ParseCertificate(cert.Certificate[0]) - assert.Nil(c.T(), err) - - ski, err := SkiFromCertificate(leaf) - assert.Nil(c.T(), err) - assert.NotEqual(c.T(), "", ski) - - cert, err = createInvalidCertificate("unit", "org", "DE", "CN") - assert.Nil(c.T(), err) - - leaf, err = x509.ParseCertificate(cert.Certificate[0]) - assert.Nil(c.T(), err) - - ski, err = SkiFromCertificate(leaf) - assert.NotNil(c.T(), err) - assert.Equal(c.T(), "", ski) -} - -func createInvalidCertificate(organizationalUnit, organization, country, commonName string) (tls.Certificate, error) { - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return tls.Certificate{}, err - } - - // Create the EEBUS service SKI using the private key - asn1, err := x509.MarshalECPrivateKey(privateKey) - if err != nil { - return tls.Certificate{}, err - } - // SHIP 12.2: Required to be created according to RFC 3280 4.2.1.2 - ski := sha1.Sum(asn1) - - subject := pkix.Name{ - OrganizationalUnit: []string{organizationalUnit}, - Organization: []string{organization}, - Country: []string{country}, - CommonName: commonName, - } - - // Create a random serial big int value - maxValue := new(big.Int) - maxValue.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxValue, big.NewInt(1)) - serialNumber, err := rand.Int(rand.Reader, maxValue) - if err != nil { - return tls.Certificate{}, err - } - - template := x509.Certificate{ - SignatureAlgorithm: x509.ECDSAWithSHA256, - SerialNumber: serialNumber, - Subject: subject, - NotBefore: time.Now(), // Valid starting now - NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10), // Valid for 10 years - KeyUsage: x509.KeyUsageDigitalSignature, - BasicConstraintsValid: true, - IsCA: true, - SubjectKeyId: ski[:19], - } - - certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) - if err != nil { - return tls.Certificate{}, err - } - - tlsCertificate := tls.Certificate{ - Certificate: [][]byte{certBytes}, - PrivateKey: privateKey, - SupportedSignatureAlgorithms: []tls.SignatureScheme{tls.ECDSAWithP256AndSHA256}, - } - - return tlsCertificate, nil -} diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 4c975faa..5863df4a 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -14,8 +14,8 @@ import ( "time" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" "github.com/enbility/eebus-go/service" + "github.com/enbility/ship-go/cert" "github.com/enbility/spine-go/model" ) diff --git a/cmd/hems/main.go b/cmd/hems/main.go index a8822158..93d6afb4 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -14,8 +14,8 @@ import ( "time" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" "github.com/enbility/eebus-go/service" + "github.com/enbility/ship-go/cert" "github.com/enbility/spine-go/model" ) diff --git a/go.mod b/go.mod index e9b32196..ea04359b 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,9 @@ module github.com/enbility/eebus-go go 1.18 require ( - github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6 + github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c github.com/gorilla/websocket v1.5.1 - github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 ) @@ -17,6 +16,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed // indirect github.com/miekg/dns v1.1.57 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/date v1.20.5 // indirect diff --git a/go.sum b/go.sum index c226784a..cf25f9aa 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6 h1:QyiMPmuUTL5TCe2Ax4anLtzEn7gvsP/hPjEwfpMqmWM= -github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6/go.mod h1:ZlzDQ8pDzhxT9r+6XMmU50fzgyuFnj9RyWWugJRB8s0= +github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c h1:o9u0pYg0iyVW+iLtJZ5Xpico3bfv71zNowPARy3CZDY= +github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/mocks/MdnsSearch.go b/mocks/MdnsSearch.go deleted file mode 100644 index ac749dcf..00000000 --- a/mocks/MdnsSearch.go +++ /dev/null @@ -1,68 +0,0 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. - -package mocks - -import ( - api "github.com/enbility/eebus-go/api" - mock "github.com/stretchr/testify/mock" -) - -// MdnsSearch is an autogenerated mock type for the MdnsSearch type -type MdnsSearch struct { - mock.Mock -} - -type MdnsSearch_Expecter struct { - mock *mock.Mock -} - -func (_m *MdnsSearch) EXPECT() *MdnsSearch_Expecter { - return &MdnsSearch_Expecter{mock: &_m.Mock} -} - -// ReportMdnsEntries provides a mock function with given fields: entries -func (_m *MdnsSearch) ReportMdnsEntries(entries map[string]*api.MdnsEntry) { - _m.Called(entries) -} - -// MdnsSearch_ReportMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReportMdnsEntries' -type MdnsSearch_ReportMdnsEntries_Call struct { - *mock.Call -} - -// ReportMdnsEntries is a helper method to define mock.On call -// - entries map[string]*api.MdnsEntry -func (_e *MdnsSearch_Expecter) ReportMdnsEntries(entries interface{}) *MdnsSearch_ReportMdnsEntries_Call { - return &MdnsSearch_ReportMdnsEntries_Call{Call: _e.mock.On("ReportMdnsEntries", entries)} -} - -func (_c *MdnsSearch_ReportMdnsEntries_Call) Run(run func(entries map[string]*api.MdnsEntry)) *MdnsSearch_ReportMdnsEntries_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]*api.MdnsEntry)) - }) - return _c -} - -func (_c *MdnsSearch_ReportMdnsEntries_Call) Return() *MdnsSearch_ReportMdnsEntries_Call { - _c.Call.Return() - return _c -} - -func (_c *MdnsSearch_ReportMdnsEntries_Call) RunAndReturn(run func(map[string]*api.MdnsEntry)) *MdnsSearch_ReportMdnsEntries_Call { - _c.Call.Return(run) - return _c -} - -// NewMdnsSearch creates a new instance of MdnsSearch. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMdnsSearch(t interface { - mock.TestingT - Cleanup(func()) -}) *MdnsSearch { - mock := &MdnsSearch{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/mocks/MdnsService.go b/mocks/MdnsService.go deleted file mode 100644 index c0dec29a..00000000 --- a/mocks/MdnsService.go +++ /dev/null @@ -1,255 +0,0 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. - -package mocks - -import ( - api "github.com/enbility/eebus-go/api" - mock "github.com/stretchr/testify/mock" -) - -// MdnsService is an autogenerated mock type for the MdnsService type -type MdnsService struct { - mock.Mock -} - -type MdnsService_Expecter struct { - mock *mock.Mock -} - -func (_m *MdnsService) EXPECT() *MdnsService_Expecter { - return &MdnsService_Expecter{mock: &_m.Mock} -} - -// AnnounceMdnsEntry provides a mock function with given fields: -func (_m *MdnsService) AnnounceMdnsEntry() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for AnnounceMdnsEntry") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MdnsService_AnnounceMdnsEntry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AnnounceMdnsEntry' -type MdnsService_AnnounceMdnsEntry_Call struct { - *mock.Call -} - -// AnnounceMdnsEntry is a helper method to define mock.On call -func (_e *MdnsService_Expecter) AnnounceMdnsEntry() *MdnsService_AnnounceMdnsEntry_Call { - return &MdnsService_AnnounceMdnsEntry_Call{Call: _e.mock.On("AnnounceMdnsEntry")} -} - -func (_c *MdnsService_AnnounceMdnsEntry_Call) Run(run func()) *MdnsService_AnnounceMdnsEntry_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MdnsService_AnnounceMdnsEntry_Call) Return(_a0 error) *MdnsService_AnnounceMdnsEntry_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MdnsService_AnnounceMdnsEntry_Call) RunAndReturn(run func() error) *MdnsService_AnnounceMdnsEntry_Call { - _c.Call.Return(run) - return _c -} - -// RegisterMdnsSearch provides a mock function with given fields: cb -func (_m *MdnsService) RegisterMdnsSearch(cb api.MdnsSearch) { - _m.Called(cb) -} - -// MdnsService_RegisterMdnsSearch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterMdnsSearch' -type MdnsService_RegisterMdnsSearch_Call struct { - *mock.Call -} - -// RegisterMdnsSearch is a helper method to define mock.On call -// - cb api.MdnsSearch -func (_e *MdnsService_Expecter) RegisterMdnsSearch(cb interface{}) *MdnsService_RegisterMdnsSearch_Call { - return &MdnsService_RegisterMdnsSearch_Call{Call: _e.mock.On("RegisterMdnsSearch", cb)} -} - -func (_c *MdnsService_RegisterMdnsSearch_Call) Run(run func(cb api.MdnsSearch)) *MdnsService_RegisterMdnsSearch_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.MdnsSearch)) - }) - return _c -} - -func (_c *MdnsService_RegisterMdnsSearch_Call) Return() *MdnsService_RegisterMdnsSearch_Call { - _c.Call.Return() - return _c -} - -func (_c *MdnsService_RegisterMdnsSearch_Call) RunAndReturn(run func(api.MdnsSearch)) *MdnsService_RegisterMdnsSearch_Call { - _c.Call.Return(run) - return _c -} - -// SetupMdnsService provides a mock function with given fields: -func (_m *MdnsService) SetupMdnsService() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SetupMdnsService") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MdnsService_SetupMdnsService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetupMdnsService' -type MdnsService_SetupMdnsService_Call struct { - *mock.Call -} - -// SetupMdnsService is a helper method to define mock.On call -func (_e *MdnsService_Expecter) SetupMdnsService() *MdnsService_SetupMdnsService_Call { - return &MdnsService_SetupMdnsService_Call{Call: _e.mock.On("SetupMdnsService")} -} - -func (_c *MdnsService_SetupMdnsService_Call) Run(run func()) *MdnsService_SetupMdnsService_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MdnsService_SetupMdnsService_Call) Return(_a0 error) *MdnsService_SetupMdnsService_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MdnsService_SetupMdnsService_Call) RunAndReturn(run func() error) *MdnsService_SetupMdnsService_Call { - _c.Call.Return(run) - return _c -} - -// ShutdownMdnsService provides a mock function with given fields: -func (_m *MdnsService) ShutdownMdnsService() { - _m.Called() -} - -// MdnsService_ShutdownMdnsService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ShutdownMdnsService' -type MdnsService_ShutdownMdnsService_Call struct { - *mock.Call -} - -// ShutdownMdnsService is a helper method to define mock.On call -func (_e *MdnsService_Expecter) ShutdownMdnsService() *MdnsService_ShutdownMdnsService_Call { - return &MdnsService_ShutdownMdnsService_Call{Call: _e.mock.On("ShutdownMdnsService")} -} - -func (_c *MdnsService_ShutdownMdnsService_Call) Run(run func()) *MdnsService_ShutdownMdnsService_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MdnsService_ShutdownMdnsService_Call) Return() *MdnsService_ShutdownMdnsService_Call { - _c.Call.Return() - return _c -} - -func (_c *MdnsService_ShutdownMdnsService_Call) RunAndReturn(run func()) *MdnsService_ShutdownMdnsService_Call { - _c.Call.Return(run) - return _c -} - -// UnannounceMdnsEntry provides a mock function with given fields: -func (_m *MdnsService) UnannounceMdnsEntry() { - _m.Called() -} - -// MdnsService_UnannounceMdnsEntry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnannounceMdnsEntry' -type MdnsService_UnannounceMdnsEntry_Call struct { - *mock.Call -} - -// UnannounceMdnsEntry is a helper method to define mock.On call -func (_e *MdnsService_Expecter) UnannounceMdnsEntry() *MdnsService_UnannounceMdnsEntry_Call { - return &MdnsService_UnannounceMdnsEntry_Call{Call: _e.mock.On("UnannounceMdnsEntry")} -} - -func (_c *MdnsService_UnannounceMdnsEntry_Call) Run(run func()) *MdnsService_UnannounceMdnsEntry_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MdnsService_UnannounceMdnsEntry_Call) Return() *MdnsService_UnannounceMdnsEntry_Call { - _c.Call.Return() - return _c -} - -func (_c *MdnsService_UnannounceMdnsEntry_Call) RunAndReturn(run func()) *MdnsService_UnannounceMdnsEntry_Call { - _c.Call.Return(run) - return _c -} - -// UnregisterMdnsSearch provides a mock function with given fields: cb -func (_m *MdnsService) UnregisterMdnsSearch(cb api.MdnsSearch) { - _m.Called(cb) -} - -// MdnsService_UnregisterMdnsSearch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnregisterMdnsSearch' -type MdnsService_UnregisterMdnsSearch_Call struct { - *mock.Call -} - -// UnregisterMdnsSearch is a helper method to define mock.On call -// - cb api.MdnsSearch -func (_e *MdnsService_Expecter) UnregisterMdnsSearch(cb interface{}) *MdnsService_UnregisterMdnsSearch_Call { - return &MdnsService_UnregisterMdnsSearch_Call{Call: _e.mock.On("UnregisterMdnsSearch", cb)} -} - -func (_c *MdnsService_UnregisterMdnsSearch_Call) Run(run func(cb api.MdnsSearch)) *MdnsService_UnregisterMdnsSearch_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.MdnsSearch)) - }) - return _c -} - -func (_c *MdnsService_UnregisterMdnsSearch_Call) Return() *MdnsService_UnregisterMdnsSearch_Call { - _c.Call.Return() - return _c -} - -func (_c *MdnsService_UnregisterMdnsSearch_Call) RunAndReturn(run func(api.MdnsSearch)) *MdnsService_UnregisterMdnsSearch_Call { - _c.Call.Return(run) - return _c -} - -// NewMdnsService creates a new instance of MdnsService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMdnsService(t interface { - mock.TestingT - Cleanup(func()) -}) *MdnsService { - mock := &MdnsService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/mocks/ServiceProvider.go b/mocks/ServiceProvider.go index 1e4c3888..2d0e4759 100644 --- a/mocks/ServiceProvider.go +++ b/mocks/ServiceProvider.go @@ -5,6 +5,8 @@ package mocks import ( api "github.com/enbility/eebus-go/api" mock "github.com/stretchr/testify/mock" + + ship_goapi "github.com/enbility/ship-go/api" ) // ServiceProvider is an autogenerated mock type for the ServiceProvider type @@ -201,7 +203,7 @@ func (_c *ServiceProvider_ServiceShipIDUpdate_Call) RunAndReturn(run func(string } // VisibleMDNSRecordsUpdated provides a mock function with given fields: entries -func (_m *ServiceProvider) VisibleMDNSRecordsUpdated(entries []*api.MdnsEntry) { +func (_m *ServiceProvider) VisibleMDNSRecordsUpdated(entries []*ship_goapi.MdnsEntry) { _m.Called(entries) } @@ -211,14 +213,14 @@ type ServiceProvider_VisibleMDNSRecordsUpdated_Call struct { } // VisibleMDNSRecordsUpdated is a helper method to define mock.On call -// - entries []*api.MdnsEntry +// - entries []*ship_goapi.MdnsEntry func (_e *ServiceProvider_Expecter) VisibleMDNSRecordsUpdated(entries interface{}) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { return &ServiceProvider_VisibleMDNSRecordsUpdated_Call{Call: _e.mock.On("VisibleMDNSRecordsUpdated", entries)} } -func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) Run(run func(entries []*api.MdnsEntry)) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { +func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) Run(run func(entries []*ship_goapi.MdnsEntry)) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]*api.MdnsEntry)) + run(args[0].([]*ship_goapi.MdnsEntry)) }) return _c } @@ -228,7 +230,7 @@ func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) Return() *ServiceProvi return _c } -func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) RunAndReturn(run func([]*api.MdnsEntry)) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { +func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) RunAndReturn(run func([]*ship_goapi.MdnsEntry)) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { _c.Call.Return(run) return _c } diff --git a/mocks/mockgen_api.go b/mocks/mockgen_api.go index fdfb3fc0..3100eb65 100644 --- a/mocks/mockgen_api.go +++ b/mocks/mockgen_api.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/enbility/eebus-go/api (interfaces: ServiceProvider,MdnsService) +// Source: github.com/enbility/eebus-go/api (interfaces: ServiceProvider) // // Generated by this command: // -// mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider,MdnsService +// mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider // // Package mocks is a generated GoMock package. @@ -13,6 +13,7 @@ import ( reflect "reflect" api "github.com/enbility/eebus-go/api" + api0 "github.com/enbility/ship-go/api" gomock "go.uber.org/mock/gomock" ) @@ -102,7 +103,7 @@ func (mr *MockServiceProviderMockRecorder) ServiceShipIDUpdate(arg0, arg1 any) * } // VisibleMDNSRecordsUpdated mocks base method. -func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []*api.MdnsEntry) { +func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []*api0.MdnsEntry) { m.ctrl.T.Helper() m.ctrl.Call(m, "VisibleMDNSRecordsUpdated", arg0) } @@ -112,102 +113,3 @@ func (mr *MockServiceProviderMockRecorder) VisibleMDNSRecordsUpdated(arg0 any) * mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleMDNSRecordsUpdated", reflect.TypeOf((*MockServiceProvider)(nil).VisibleMDNSRecordsUpdated), arg0) } - -// MockMdnsService is a mock of MdnsService interface. -type MockMdnsService struct { - ctrl *gomock.Controller - recorder *MockMdnsServiceMockRecorder -} - -// MockMdnsServiceMockRecorder is the mock recorder for MockMdnsService. -type MockMdnsServiceMockRecorder struct { - mock *MockMdnsService -} - -// NewMockMdnsService creates a new mock instance. -func NewMockMdnsService(ctrl *gomock.Controller) *MockMdnsService { - mock := &MockMdnsService{ctrl: ctrl} - mock.recorder = &MockMdnsServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockMdnsService) EXPECT() *MockMdnsServiceMockRecorder { - return m.recorder -} - -// AnnounceMdnsEntry mocks base method. -func (m *MockMdnsService) AnnounceMdnsEntry() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AnnounceMdnsEntry") - ret0, _ := ret[0].(error) - return ret0 -} - -// AnnounceMdnsEntry indicates an expected call of AnnounceMdnsEntry. -func (mr *MockMdnsServiceMockRecorder) AnnounceMdnsEntry() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnnounceMdnsEntry", reflect.TypeOf((*MockMdnsService)(nil).AnnounceMdnsEntry)) -} - -// RegisterMdnsSearch mocks base method. -func (m *MockMdnsService) RegisterMdnsSearch(arg0 api.MdnsSearch) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RegisterMdnsSearch", arg0) -} - -// RegisterMdnsSearch indicates an expected call of RegisterMdnsSearch. -func (mr *MockMdnsServiceMockRecorder) RegisterMdnsSearch(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).RegisterMdnsSearch), arg0) -} - -// SetupMdnsService mocks base method. -func (m *MockMdnsService) SetupMdnsService() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetupMdnsService") - ret0, _ := ret[0].(error) - return ret0 -} - -// SetupMdnsService indicates an expected call of SetupMdnsService. -func (mr *MockMdnsServiceMockRecorder) SetupMdnsService() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupMdnsService", reflect.TypeOf((*MockMdnsService)(nil).SetupMdnsService)) -} - -// ShutdownMdnsService mocks base method. -func (m *MockMdnsService) ShutdownMdnsService() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ShutdownMdnsService") -} - -// ShutdownMdnsService indicates an expected call of ShutdownMdnsService. -func (mr *MockMdnsServiceMockRecorder) ShutdownMdnsService() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShutdownMdnsService", reflect.TypeOf((*MockMdnsService)(nil).ShutdownMdnsService)) -} - -// UnannounceMdnsEntry mocks base method. -func (m *MockMdnsService) UnannounceMdnsEntry() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "UnannounceMdnsEntry") -} - -// UnannounceMdnsEntry indicates an expected call of UnannounceMdnsEntry. -func (mr *MockMdnsServiceMockRecorder) UnannounceMdnsEntry() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnannounceMdnsEntry", reflect.TypeOf((*MockMdnsService)(nil).UnannounceMdnsEntry)) -} - -// UnregisterMdnsSearch mocks base method. -func (m *MockMdnsService) UnregisterMdnsSearch(arg0 api.MdnsSearch) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "UnregisterMdnsSearch", arg0) -} - -// UnregisterMdnsSearch indicates an expected call of UnregisterMdnsSearch. -func (mr *MockMdnsServiceMockRecorder) UnregisterMdnsSearch(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterMdnsSearch", reflect.TypeOf((*MockMdnsService)(nil).UnregisterMdnsSearch), arg0) -} diff --git a/service/hub.go b/service/hub.go index 2076648f..d9d033e1 100644 --- a/service/hub.go +++ b/service/hub.go @@ -16,8 +16,8 @@ import ( "time" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/cert" "github.com/enbility/ship-go/logging" shipmodel "github.com/enbility/ship-go/model" "github.com/enbility/ship-go/ship" @@ -27,8 +27,6 @@ import ( "github.com/gorilla/websocket" ) -const shipWebsocketPath = "/ship/" - // used for randomizing the connection initiation delay // this limits the possibility of concurrent connection attempts from both sides type connectionInitiationDelayTimeRange struct { @@ -64,10 +62,10 @@ type connectionsHubImpl struct { httpServer *http.Server // Handling mDNS related tasks - mdns api.MdnsService + mdns shipapi.MdnsService // list of currently known/reported mDNS entries - knownMdnsEntries []*api.MdnsEntry + knownMdnsEntries []*shipapi.MdnsEntry spineLocalDevice spineapi.DeviceLocal @@ -77,13 +75,13 @@ type connectionsHubImpl struct { muxMdns sync.Mutex } -func newConnectionsHub(serviceProvider api.ServiceProvider, mdns api.MdnsService, spineLocalDevice spineapi.DeviceLocal, configuration *api.Configuration, localService *api.ServiceDetails) api.ConnectionsHub { +func newConnectionsHub(serviceProvider api.ServiceProvider, mdns shipapi.MdnsService, spineLocalDevice spineapi.DeviceLocal, configuration *api.Configuration, localService *api.ServiceDetails) api.ConnectionsHub { hub := &connectionsHubImpl{ connections: make(map[string]shipapi.ShipConnection), connectionAttemptCounter: make(map[string]int), connectionAttemptRunning: make(map[string]bool), remoteServices: make(map[string]*api.ServiceDetails), - knownMdnsEntries: make([]*api.MdnsEntry, 0), + knownMdnsEntries: make([]*shipapi.MdnsEntry, 0), serviceProvider: serviceProvider, spineLocalDevice: spineLocalDevice, configuration: configuration, @@ -657,11 +655,11 @@ func (h *connectionsHubImpl) CancelPairingWithSKI(ski string) { } // Process reported mDNS services -func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*api.MdnsEntry) { +func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*shipapi.MdnsEntry) { h.muxMdns.Lock() defer h.muxMdns.Unlock() - var mdnsEntries []*api.MdnsEntry + var mdnsEntries []*shipapi.MdnsEntry for ski, entry := range entries { mdnsEntries = append(mdnsEntries, entry) @@ -702,7 +700,7 @@ func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*api.MdnsEntry } // coordinate connection initiation attempts to a remove service -func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *api.MdnsEntry) { +func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *shipapi.MdnsEntry) { if h.isConnectionAttemptRunning(ski) { return } @@ -731,7 +729,7 @@ func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *a // invoked by coordinateConnectionInitations either with a delay or directly // when initating a pairing process -func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, entry *api.MdnsEntry) { +func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, entry *shipapi.MdnsEntry) { h.setConnectionAttemptRunning(ski, false) // check if the current counter is still the same, otherwise this counter is irrelevant @@ -763,7 +761,7 @@ func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, // attempt to establish a connection to a remote service // returns true if successful -func (h *connectionsHubImpl) initateConnection(remoteService *api.ServiceDetails, entry *api.MdnsEntry) bool { +func (h *connectionsHubImpl) initateConnection(remoteService *api.ServiceDetails, entry *shipapi.MdnsEntry) bool { var err error // try connecting via an IP address first diff --git a/service/hub_test.go b/service/hub_test.go index 0f5a473c..961f7929 100644 --- a/service/hub_test.go +++ b/service/hub_test.go @@ -19,9 +19,9 @@ import ( "time" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" "github.com/enbility/eebus-go/mocks" shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/cert" shipmocks "github.com/enbility/ship-go/mocks" shipmodel "github.com/enbility/ship-go/model" "github.com/enbility/spine-go/model" @@ -45,7 +45,7 @@ type HubSuite struct { suite.Suite serviceProvider *mocks.MockServiceProvider - mdnsService *mocks.MockMdnsService + mdnsService *shipmocks.MockMdnsService // serviceProvider *mocks.ServiceProvider // mdnsService *mocks.MdnsService @@ -87,7 +87,7 @@ func (s *HubSuite) BeforeTest(suiteName, testName string) { s.serviceProvider.EXPECT().RemoteSKIDisconnected(gomock.Any()).Return().AnyTimes() s.serviceProvider.EXPECT().AllowWaitingForTrust(gomock.Any()).Return(false).AnyTimes() - s.mdnsService = mocks.NewMockMdnsService(ctrl) + s.mdnsService = shipmocks.NewMockMdnsService(ctrl) // s.mdnsService = mocks.NewMdnsService(s.T()) s.mdnsService.EXPECT().SetupMdnsService().Return(nil).AnyTimes() s.mdnsService.EXPECT().AnnounceMdnsEntry().Return(nil).AnyTimes() @@ -441,7 +441,7 @@ func (s *HubSuite) Test_KeepThisConnection() { } func (s *HubSuite) Test_prepareConnectionInitiation() { - entry := &api.MdnsEntry{ + entry := &shipapi.MdnsEntry{ Ski: s.remoteSki, Host: "somehost", } @@ -465,7 +465,7 @@ func (s *HubSuite) Test_prepareConnectionInitiation() { } func (s *HubSuite) Test_InitiateConnection() { - entry := &api.MdnsEntry{ + entry := &shipapi.MdnsEntry{ Ski: s.remoteSki, Host: "somehost", } @@ -563,19 +563,19 @@ func (s *HubSuite) Test_ReportMdnsEntries() { testski1 := "test1" testski2 := "test2" - entries := make(map[string]*api.MdnsEntry) + entries := make(map[string]*shipapi.MdnsEntry) s.serviceProvider.EXPECT().VisibleMDNSRecordsUpdated(gomock.Any()).AnyTimes() s.sut.ReportMdnsEntries(entries) - entries[testski1] = &api.MdnsEntry{ + entries[testski1] = &shipapi.MdnsEntry{ Ski: testski1, } service1 := s.sut.ServiceForSKI(testski1) service1.Trusted = true service1.IPv4 = "127.0.0.1" - entries[testski2] = &api.MdnsEntry{ + entries[testski2] = &shipapi.MdnsEntry{ Ski: testski2, } service2 := s.sut.ServiceForSKI(testski2) diff --git a/service/mdns.go b/service/mdns.go deleted file mode 100644 index 32ba06d0..00000000 --- a/service/mdns.go +++ /dev/null @@ -1,390 +0,0 @@ -package service - -import ( - "errors" - "fmt" - "net" - "os" - "os/signal" - "sync" - "syscall" - - "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/util" - shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/logging" - "github.com/enbility/ship-go/mdns" - "github.com/holoplot/go-avahi" -) - -type mdnsManager struct { - configuration *api.Configuration - ski string - - isAnnounced bool - isSearchingServices bool - - cancelChan chan bool - - // the currently available mDNS entries with the SKI as the key in the map - entries map[string]*api.MdnsEntry - - // the registered callback, only connectionsHub is using this - searchDelegate api.MdnsSearch - - mdnsProvider shipapi.MdnsProvider - - mux sync.Mutex - entriesMux sync.Mutex -} - -func newMDNS(ski string, configuration *api.Configuration) *mdnsManager { - m := &mdnsManager{ - ski: ski, - configuration: configuration, - entries: make(map[string]*api.MdnsEntry), - cancelChan: make(chan bool), - } - - return m -} - -// Return allowed interfaces for mDNS -func (m *mdnsManager) interfaces() ([]net.Interface, []int32, error) { - var ifaces []net.Interface - var ifaceIndexes []int32 - - if len(m.configuration.Interfaces()) > 0 { - ifaces = make([]net.Interface, len(m.configuration.Interfaces())) - ifaceIndexes = make([]int32, len(m.configuration.Interfaces())) - for i, ifaceName := range m.configuration.Interfaces() { - iface, err := net.InterfaceByName(ifaceName) - if err != nil { - return nil, nil, err - } - ifaces[i] = *iface - ifaceIndexes[i] = int32(iface.Index) - } - } - - if len(ifaces) == 0 { - ifaces = nil - ifaceIndexes = []int32{avahi.InterfaceUnspec} - } - - return ifaces, ifaceIndexes, nil -} - -var _ api.MdnsService = (*mdnsManager)(nil) - -func (m *mdnsManager) SetupMdnsService() error { - ifaces, ifaceIndexes, err := m.interfaces() - if err != nil { - return err - } - - m.mdnsProvider = mdns.NewAvahiProvider(ifaceIndexes) - if !m.mdnsProvider.CheckAvailability() { - m.mdnsProvider.Shutdown() - - // Avahi is not availble, use Zeroconf - m.mdnsProvider = mdns.NewZeroconfProvider(ifaces) - if !m.mdnsProvider.CheckAvailability() { - return errors.New("No mDNS provider available") - } - } - - // on startup always start mDNS announcement - if err := m.AnnounceMdnsEntry(); err != nil { - return err - } - - // catch signals - go func() { - signalC := make(chan os.Signal, 1) - signal.Notify(signalC, os.Interrupt, syscall.SIGTERM) - - <-signalC // wait for signal - - m.ShutdownMdnsService() - }() - - return nil -} - -// Announces the service to the network via mDNS -// A CEM service should always invoke this on startup -// Any other service should only invoke this whenever it is not connected to a CEM service -func (m *mdnsManager) AnnounceMdnsEntry() error { - if m.isAnnounced { - return nil - } - - serviceIdentifier := m.configuration.Identifier() - - txt := []string{ // SHIP 7.3.2 - "txtvers=1", - "path=" + shipWebsocketPath, - "id=" + serviceIdentifier, - "ski=" + m.ski, - "brand=" + m.configuration.DeviceBrand(), - "model=" + m.configuration.DeviceModel(), - "type=" + string(m.configuration.DeviceType()), - "register=" + fmt.Sprintf("%v", m.configuration.RegisterAutoAccept()), - } - - logging.Log().Debug("mdns: announce") - - serviceName := m.configuration.MdnsServiceName() - - if err := m.mdnsProvider.Announce(serviceName, m.configuration.Port(), txt); err != nil { - logging.Log().Debug("mdns: failure announcing service", err) - return err - } - - m.isAnnounced = true - - return nil -} - -// Stop the mDNS announcement on the network -func (m *mdnsManager) UnannounceMdnsEntry() { - if !m.isAnnounced { - return - } - - m.mdnsProvider.Unannounce() - logging.Log().Debug("mdns: stop announcement") - - m.isAnnounced = false -} - -// Shutdown all of mDNS -func (m *mdnsManager) ShutdownMdnsService() { - m.UnannounceMdnsEntry() - m.stopResolvingEntries() - - m.mdnsProvider.Shutdown() - m.mdnsProvider = nil -} - -func (m *mdnsManager) setIsSearchingServices(enable bool) { - m.mux.Lock() - defer m.mux.Unlock() - - m.isSearchingServices = enable -} - -func (m *mdnsManager) mdnsEntries() map[string]*api.MdnsEntry { - m.entriesMux.Lock() - defer m.entriesMux.Unlock() - - return m.entries -} - -func (m *mdnsManager) copyMdnsEntries() map[string]*api.MdnsEntry { - m.entriesMux.Lock() - defer m.entriesMux.Unlock() - - mdnsEntries := make(map[string]*api.MdnsEntry) - for k, v := range m.entries { - newEntry := &api.MdnsEntry{} - util.DeepCopy[*api.MdnsEntry](v, newEntry) - mdnsEntries[k] = newEntry - } - - return mdnsEntries -} - -func (m *mdnsManager) mdnsEntry(ski string) (*api.MdnsEntry, bool) { - m.entriesMux.Lock() - defer m.entriesMux.Unlock() - - entry, ok := m.entries[ski] - return entry, ok -} - -func (m *mdnsManager) setMdnsEntry(ski string, entry *api.MdnsEntry) { - m.entriesMux.Lock() - defer m.entriesMux.Unlock() - - m.entries[ski] = entry -} - -func (m *mdnsManager) removeMdnsEntry(ski string) { - m.entriesMux.Lock() - defer m.entriesMux.Unlock() - - delete(m.entries, ski) -} - -// Register a callback to be invoked for found mDNS entries -func (m *mdnsManager) RegisterMdnsSearch(cb api.MdnsSearch) { - m.mux.Lock() - if m.searchDelegate != cb { - m.searchDelegate = cb - } - m.mux.Unlock() - - if !m.isSearchingServices { - m.setIsSearchingServices(true) - m.resolveEntries() - return - } - - // do we already know some entries? - if len(m.mdnsEntries()) == 0 { - return - } - - // maybe entries are already found - mdnsEntries := m.copyMdnsEntries() - - go m.searchDelegate.ReportMdnsEntries(mdnsEntries) -} - -// Remove a callback for found mDNS entries and stop searching if no callbacks are left -func (m *mdnsManager) UnregisterMdnsSearch(cb api.MdnsSearch) { - m.mux.Lock() - defer m.mux.Unlock() - - m.searchDelegate = nil - - m.stopResolvingEntries() -} - -// search for mDNS entries and report them -func (m *mdnsManager) resolveEntries() { - if m.mdnsProvider == nil { - m.setIsSearchingServices(false) - return - } - go func() { - logging.Log().Debug("mdns: start search") - m.mdnsProvider.ResolveEntries(m.cancelChan, m.processMdnsEntry) - - m.setIsSearchingServices(false) - }() -} - -// stop searching for mDNS entries -func (m *mdnsManager) stopResolvingEntries() { - if m.cancelChan == nil { - return - } - - if util.IsChannelClosed(m.cancelChan) { - return - } - - logging.Log().Debug("mdns: stop search") - - m.cancelChan <- true -} - -// process an mDNS entry and manage mDNS entries map -func (m *mdnsManager) processMdnsEntry(elements map[string]string, name, host string, addresses []net.IP, port int, remove bool) { - // check for mandatory text elements - mapItems := []string{"txtvers", "id", "path", "ski", "register"} - for _, item := range mapItems { - if _, ok := elements[item]; !ok { - return - } - } - - txtvers := elements["txtvers"] - // value of mandatory txtvers has to be 1 or the response be ignored: SHIP 7.3.2 - if txtvers != "1" { - return - } - - identifier := elements["id"] - path := elements["path"] - ski := elements["ski"] - - // ignore own service - if ski == m.ski { - return - } - - register := elements["register"] - // register has to be a boolean - if register != "true" && register != "false" { - return - } - - var deviceType, model, brand string - - if _, ok := elements["brand"]; ok { - brand = elements["brand"] - } - if _, ok := elements["type"]; ok { - deviceType = elements["type"] - } - if _, ok := elements["model"]; ok { - model = elements["model"] - } - - m.mux.Lock() - defer m.mux.Unlock() - - updated := true - - entry, exists := m.mdnsEntry(ski) - - if remove && exists { - // remove - // there will be a remove for each address with avahi, but we'll delete it right away - m.removeMdnsEntry(ski) - } else if exists { - // update - updated = false - - // avahi sends an item for each network address, merge them - - // we assume only network addresses are added - for _, address := range addresses { - // only add if it is not added yet - isNewElement := true - - for _, item := range entry.Addresses { - if item.String() == address.String() { - isNewElement = false - break - } - } - - if isNewElement { - entry.Addresses = append(entry.Addresses, address) - updated = true - } - } - - m.setMdnsEntry(ski, entry) - } else if !exists && !remove { - // new - newEntry := &api.MdnsEntry{ - Name: name, - Ski: ski, - Identifier: identifier, - Path: path, - Register: register == "true", - Brand: brand, - Type: deviceType, - Model: model, - Host: host, - Port: port, - Addresses: addresses, - } - m.setMdnsEntry(ski, newEntry) - - logging.Log().Debug("ski:", ski, "name:", name, "brand:", brand, "model:", model, "typ:", deviceType, "identifier:", identifier, "register:", register, "host:", host, "port:", port, "addresses:", addresses) - } else { - return - } - - if m.searchDelegate != nil && updated { - entries := m.copyMdnsEntries() - go m.searchDelegate.ReportMdnsEntries(entries) - } -} diff --git a/service/mdns_test.go b/service/mdns_test.go deleted file mode 100644 index db5c5bb9..00000000 --- a/service/mdns_test.go +++ /dev/null @@ -1,203 +0,0 @@ -package service - -import ( - "net" - "testing" - "time" - - "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" - "github.com/enbility/eebus-go/mocks" - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" -) - -func TestMdnsSuite(t *testing.T) { - suite.Run(t, new(MdnsSuite)) -} - -type MdnsSuite struct { - suite.Suite - - sut *mdnsManager - - config *api.Configuration - - mdnsService *mocks.MdnsService - mdnsSearch *mocks.MdnsSearch - mdnsProvider *mocks.MdnsProvider -} - -func (s *MdnsSuite) SetupSuite() {} -func (s *MdnsSuite) TearDownTest() {} - -func (s *MdnsSuite) BeforeTest(suiteName, testName string) { - s.mdnsService = mocks.NewMdnsService(s.T()) - - s.mdnsSearch = mocks.NewMdnsSearch(s.T()) - s.mdnsSearch.EXPECT().ReportMdnsEntries(mock.Anything).Maybe() - - s.mdnsProvider = mocks.NewMdnsProvider(s.T()) - s.mdnsProvider.On("ResolveEntries", mock.Anything, mock.Anything).Maybe().Return() - s.mdnsProvider.On("Shutdown").Maybe().Return() - - certificate, _ := cert.CreateCertificate("unit", "org", "DE", "CN") - - s.config, _ = api.NewConfiguration( - "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, - []model.EntityTypeType{model.EntityTypeTypeCEM}, 4729, certificate, 230.0, time.Second*4) - - s.sut = newMDNS("test", s.config) - s.sut.mdnsProvider = s.mdnsProvider -} - -func (s *MdnsSuite) Test_SetupMdnsService() { - err := s.sut.SetupMdnsService() - assert.Nil(s.T(), err) - - assert.Equal(s.T(), true, s.sut.isAnnounced) - - s.sut.UnannounceMdnsEntry() - assert.Equal(s.T(), false, s.sut.isAnnounced) - - s.sut.UnannounceMdnsEntry() - assert.Equal(s.T(), false, s.sut.isAnnounced) - - ifaces, err := net.Interfaces() - assert.NotEqual(s.T(), 0, len(ifaces)) - assert.Nil(s.T(), err) - - // we don't have access to iface names on CI - if !util.IsRunningOnCI() { - s.config.SetInterfaces([]string{ifaces[0].Name}) - err = s.sut.SetupMdnsService() - assert.Nil(s.T(), err) - } - - s.config.SetInterfaces([]string{"noifacename"}) - err = s.sut.SetupMdnsService() - assert.NotNil(s.T(), err) - - assert.Equal(s.T(), false, s.sut.isSearchingServices) - s.sut.setIsSearchingServices(true) - assert.Equal(s.T(), true, s.sut.isSearchingServices) - -} - -func (s *MdnsSuite) Test_ShutdownMdnsService() { - s.sut.ShutdownMdnsService() - assert.Nil(s.T(), s.sut.mdnsProvider) -} - -func (s *MdnsSuite) Test_MdnsEntry() { - testSki := "test" - - entries := s.sut.mdnsEntries() - assert.Equal(s.T(), 0, len(entries)) - - entry := &api.MdnsEntry{ - Ski: testSki, - } - - s.sut.setMdnsEntry(testSki, entry) - entries = s.sut.mdnsEntries() - assert.Equal(s.T(), 1, len(entries)) - - theEntry, ok := s.sut.mdnsEntry(testSki) - assert.Equal(s.T(), true, ok) - assert.NotNil(s.T(), theEntry) - - copyEntries := s.sut.copyMdnsEntries() - assert.Equal(s.T(), 1, len(copyEntries)) - - s.sut.removeMdnsEntry(testSki) - entries = s.sut.mdnsEntries() - assert.Equal(s.T(), 0, len(entries)) - assert.Equal(s.T(), 1, len(copyEntries)) -} - -func (s *MdnsSuite) Test_MdnsSearch() { - assert.Equal(s.T(), false, s.sut.isSearchingServices) - s.sut.RegisterMdnsSearch(s.mdnsSearch) - assert.Equal(s.T(), true, s.sut.isSearchingServices) - - s.sut.setIsSearchingServices(true) - assert.Equal(s.T(), true, s.sut.isSearchingServices) - - s.sut.RegisterMdnsSearch(s.mdnsSearch) - - testSki := "test" - - entry := &api.MdnsEntry{ - Ski: testSki, - } - s.sut.setMdnsEntry(testSki, entry) - entries := s.sut.mdnsEntries() - assert.Equal(s.T(), 1, len(entries)) - - s.sut.setIsSearchingServices(false) - - s.sut.RegisterMdnsSearch(s.mdnsSearch) - - // wait a bit as ResolveEntries is called in a goroutine - time.Sleep(time.Millisecond * 200) - - s.sut.UnregisterMdnsSearch(s.mdnsSearch) -} - -func (s *MdnsSuite) Test_ProcessMdnsEntry() { - elements := make(map[string]string, 1) - - name := "name" - host := "host" - ips := []net.IP{} - port := 4567 - - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) - - elements["txtvers"] = "2" - elements["id"] = "id" - elements["path"] = "/ship" - elements["ski"] = "testski" - elements["register"] = "falsee" - - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) - - elements["txtvers"] = "1" - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) - - elements["ski"] = s.sut.ski - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) - - elements["ski"] = "testski" - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) - - elements["register"] = "false" - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 1, len(s.sut.mdnsEntries())) - - elements["brand"] = "brand" - elements["type"] = "type" - elements["model"] = "model" - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 1, len(s.sut.mdnsEntries())) - - ips = []net.IP{[]byte("127.0.0.1")} - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 1, len(s.sut.mdnsEntries())) - - s.sut.searchDelegate = s.mdnsSearch - s.sut.processMdnsEntry(elements, name, host, ips, port, false) - assert.Equal(s.T(), 1, len(s.sut.mdnsEntries())) - - s.sut.processMdnsEntry(elements, name, host, ips, port, true) - assert.Equal(s.T(), 0, len(s.sut.mdnsEntries())) -} diff --git a/service/service.go b/service/service.go index ec8cbb33..df3b8ad5 100644 --- a/service/service.go +++ b/service/service.go @@ -7,8 +7,10 @@ import ( "sync" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/cert" "github.com/enbility/ship-go/logging" + "github.com/enbility/ship-go/mdns" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -43,7 +45,7 @@ func NewEEBUSService(configuration *api.Configuration, serviceHandler api.EEBUSS var _ api.ServiceProvider = (*EEBUSServiceImpl)(nil) -func (s *EEBUSServiceImpl) VisibleMDNSRecordsUpdated(entries []*api.MdnsEntry) { +func (s *EEBUSServiceImpl) VisibleMDNSRecordsUpdated(entries []*shipapi.MdnsEntry) { var remoteServices []api.RemoteService for _, entry := range entries { @@ -181,7 +183,9 @@ func (s *EEBUSServiceImpl) Setup() error { } // setup mDNS - mdns := newMDNS(s.localService.SKI, s.configuration) + mdns := mdns.NewMDNS( + s.localService.SKI, sd.DeviceBrand(), sd.DeviceModel(), string(sd.DeviceType()), + sd.Identifier(), sd.MdnsServiceName(), sd.Port(), sd.Interfaces()) // Setup connections hub with mDNS and websocket connection handling s.connectionsHub = newConnectionsHub(s, mdns, s.spineLocalDevice, s.configuration, s.localService) diff --git a/service/service_test.go b/service/service_test.go index c144aabe..f692c77a 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -6,8 +6,9 @@ import ( "time" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" "github.com/enbility/eebus-go/mocks" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/cert" "github.com/enbility/ship-go/logging" shipmocks "github.com/enbility/ship-go/mocks" "github.com/enbility/spine-go/model" @@ -50,11 +51,11 @@ func (s *ServiceSuite) BeforeTest(suiteName, testName string) { func (s *ServiceSuite) Test_EEBUSHandler() { testSki := "test" - entry := &api.MdnsEntry{ + entry := &shipapi.MdnsEntry{ Ski: testSki, } - entries := []*api.MdnsEntry{entry} + entries := []*shipapi.MdnsEntry{entry} s.serviceHandler.EXPECT().VisibleRemoteServicesUpdated(mock.Anything, mock.Anything).Return() s.sut.VisibleMDNSRecordsUpdated(entries) From a7bd7006f4dabe682b3275fa256a7c50cfebe55a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Jan 2024 21:00:01 +0100 Subject: [PATCH 159/240] Remove non needed mock file --- mocks/MdnsProvider.go | 227 ------------------------------------------ 1 file changed, 227 deletions(-) delete mode 100644 mocks/MdnsProvider.go diff --git a/mocks/MdnsProvider.go b/mocks/MdnsProvider.go deleted file mode 100644 index 718b0b54..00000000 --- a/mocks/MdnsProvider.go +++ /dev/null @@ -1,227 +0,0 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. - -package mocks - -import ( - net "net" - - mock "github.com/stretchr/testify/mock" -) - -// MdnsProvider is an autogenerated mock type for the MdnsProvider type -type MdnsProvider struct { - mock.Mock -} - -type MdnsProvider_Expecter struct { - mock *mock.Mock -} - -func (_m *MdnsProvider) EXPECT() *MdnsProvider_Expecter { - return &MdnsProvider_Expecter{mock: &_m.Mock} -} - -// Announce provides a mock function with given fields: serviceName, port, txt -func (_m *MdnsProvider) Announce(serviceName string, port int, txt []string) error { - ret := _m.Called(serviceName, port, txt) - - if len(ret) == 0 { - panic("no return value specified for Announce") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, int, []string) error); ok { - r0 = rf(serviceName, port, txt) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MdnsProvider_Announce_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Announce' -type MdnsProvider_Announce_Call struct { - *mock.Call -} - -// Announce is a helper method to define mock.On call -// - serviceName string -// - port int -// - txt []string -func (_e *MdnsProvider_Expecter) Announce(serviceName interface{}, port interface{}, txt interface{}) *MdnsProvider_Announce_Call { - return &MdnsProvider_Announce_Call{Call: _e.mock.On("Announce", serviceName, port, txt)} -} - -func (_c *MdnsProvider_Announce_Call) Run(run func(serviceName string, port int, txt []string)) *MdnsProvider_Announce_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(int), args[2].([]string)) - }) - return _c -} - -func (_c *MdnsProvider_Announce_Call) Return(_a0 error) *MdnsProvider_Announce_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MdnsProvider_Announce_Call) RunAndReturn(run func(string, int, []string) error) *MdnsProvider_Announce_Call { - _c.Call.Return(run) - return _c -} - -// CheckAvailability provides a mock function with given fields: -func (_m *MdnsProvider) CheckAvailability() bool { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for CheckAvailability") - } - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// MdnsProvider_CheckAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckAvailability' -type MdnsProvider_CheckAvailability_Call struct { - *mock.Call -} - -// CheckAvailability is a helper method to define mock.On call -func (_e *MdnsProvider_Expecter) CheckAvailability() *MdnsProvider_CheckAvailability_Call { - return &MdnsProvider_CheckAvailability_Call{Call: _e.mock.On("CheckAvailability")} -} - -func (_c *MdnsProvider_CheckAvailability_Call) Run(run func()) *MdnsProvider_CheckAvailability_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MdnsProvider_CheckAvailability_Call) Return(_a0 bool) *MdnsProvider_CheckAvailability_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MdnsProvider_CheckAvailability_Call) RunAndReturn(run func() bool) *MdnsProvider_CheckAvailability_Call { - _c.Call.Return(run) - return _c -} - -// ResolveEntries provides a mock function with given fields: cancelChan, callback -func (_m *MdnsProvider) ResolveEntries(cancelChan chan bool, callback func(map[string]string, string, string, []net.IP, int, bool)) { - _m.Called(cancelChan, callback) -} - -// MdnsProvider_ResolveEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResolveEntries' -type MdnsProvider_ResolveEntries_Call struct { - *mock.Call -} - -// ResolveEntries is a helper method to define mock.On call -// - cancelChan chan bool -// - callback func(map[string]string , string , string , []net.IP , int , bool) -func (_e *MdnsProvider_Expecter) ResolveEntries(cancelChan interface{}, callback interface{}) *MdnsProvider_ResolveEntries_Call { - return &MdnsProvider_ResolveEntries_Call{Call: _e.mock.On("ResolveEntries", cancelChan, callback)} -} - -func (_c *MdnsProvider_ResolveEntries_Call) Run(run func(cancelChan chan bool, callback func(map[string]string, string, string, []net.IP, int, bool))) *MdnsProvider_ResolveEntries_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(chan bool), args[1].(func(map[string]string, string, string, []net.IP, int, bool))) - }) - return _c -} - -func (_c *MdnsProvider_ResolveEntries_Call) Return() *MdnsProvider_ResolveEntries_Call { - _c.Call.Return() - return _c -} - -func (_c *MdnsProvider_ResolveEntries_Call) RunAndReturn(run func(chan bool, func(map[string]string, string, string, []net.IP, int, bool))) *MdnsProvider_ResolveEntries_Call { - _c.Call.Return(run) - return _c -} - -// Shutdown provides a mock function with given fields: -func (_m *MdnsProvider) Shutdown() { - _m.Called() -} - -// MdnsProvider_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown' -type MdnsProvider_Shutdown_Call struct { - *mock.Call -} - -// Shutdown is a helper method to define mock.On call -func (_e *MdnsProvider_Expecter) Shutdown() *MdnsProvider_Shutdown_Call { - return &MdnsProvider_Shutdown_Call{Call: _e.mock.On("Shutdown")} -} - -func (_c *MdnsProvider_Shutdown_Call) Run(run func()) *MdnsProvider_Shutdown_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MdnsProvider_Shutdown_Call) Return() *MdnsProvider_Shutdown_Call { - _c.Call.Return() - return _c -} - -func (_c *MdnsProvider_Shutdown_Call) RunAndReturn(run func()) *MdnsProvider_Shutdown_Call { - _c.Call.Return(run) - return _c -} - -// Unannounce provides a mock function with given fields: -func (_m *MdnsProvider) Unannounce() { - _m.Called() -} - -// MdnsProvider_Unannounce_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Unannounce' -type MdnsProvider_Unannounce_Call struct { - *mock.Call -} - -// Unannounce is a helper method to define mock.On call -func (_e *MdnsProvider_Expecter) Unannounce() *MdnsProvider_Unannounce_Call { - return &MdnsProvider_Unannounce_Call{Call: _e.mock.On("Unannounce")} -} - -func (_c *MdnsProvider_Unannounce_Call) Run(run func()) *MdnsProvider_Unannounce_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MdnsProvider_Unannounce_Call) Return() *MdnsProvider_Unannounce_Call { - _c.Call.Return() - return _c -} - -func (_c *MdnsProvider_Unannounce_Call) RunAndReturn(run func()) *MdnsProvider_Unannounce_Call { - _c.Call.Return(run) - return _c -} - -// NewMdnsProvider creates a new instance of MdnsProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMdnsProvider(t interface { - mock.TestingT - Cleanup(func()) -}) *MdnsProvider { - mock := &MdnsProvider{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} From bb2a283dd60b554bddea70f8eb2e1e2e7daf60a7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 16 Jan 2024 13:16:35 +0100 Subject: [PATCH 160/240] Moved connectionHub to ship-go as Hub --- api/api.go | 48 +- api/connectionstate.go | 80 ---- api/connectionstate_test.go | 29 -- api/servicedetails.go | 67 --- api/servicedetails_test.go | 29 -- cmd/evse/main.go | 7 +- cmd/hems/main.go | 7 +- go.mod | 6 +- go.sum | 4 +- mocks/ConnectionsHub.go | 393 ---------------- mocks/EEBUSService.go | 38 +- mocks/EEBUSServiceHandler.go | 22 +- mocks/ServiceProvider.go | 250 ---------- mocks/mockgen_api.go | 115 ----- service/hub.go | 884 ----------------------------------- service/hub_test.go | 641 ------------------------- service/service.go | 33 +- service/service_test.go | 6 +- util/channel.go | 11 - util/helper.go | 27 -- 20 files changed, 74 insertions(+), 2623 deletions(-) delete mode 100644 api/connectionstate.go delete mode 100644 api/connectionstate_test.go delete mode 100644 api/servicedetails.go delete mode 100644 api/servicedetails_test.go delete mode 100644 mocks/ConnectionsHub.go delete mode 100644 mocks/ServiceProvider.go delete mode 100644 mocks/mockgen_api.go delete mode 100644 service/hub.go delete mode 100644 service/hub_test.go delete mode 100644 util/channel.go delete mode 100644 util/helper.go diff --git a/api/api.go b/api/api.go index 9d7b5751..71caff98 100644 --- a/api/api.go +++ b/api/api.go @@ -8,14 +8,13 @@ import ( ) //go:generate mockery -//go:generate mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider /* EEBUSService */ // interface for receiving data for specific events from EEBUSService type EEBUSServiceHandler interface { // report all currently visible EEBUS services - VisibleRemoteServicesUpdated(service EEBUSService, entries []RemoteService) + VisibleRemoteServicesUpdated(service EEBUSService, entries []shipapi.RemoteService) // report a connection to a SKI RemoteSKIConnected(service EEBUSService, ski string) @@ -31,7 +30,7 @@ type EEBUSServiceHandler interface { // Provides the current pairing state for the remote service // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process - ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) + ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) // return if the user is still able to trust the connection AllowWaitingForTrust(ski string) bool @@ -44,53 +43,16 @@ type EEBUSService interface { SetLogging(logger logging.Logging) Configuration() *Configuration - LocalService() *ServiceDetails + LocalService() *shipapi.ServiceDetails LocalDevice() spineapi.DeviceLocal - RemoteServiceForSKI(ski string) *ServiceDetails + RemoteServiceForSKI(ski string) *shipapi.ServiceDetails RegisterRemoteSKI(ski string, enable bool) InitiatePairingWithSKI(ski string) CancelPairingWithSKI(ski string) DisconnectSKI(ski string, reason string) // Passthough functions to ConnectionsHub - PairingDetailForSki(ski string) *ConnectionStateDetail + PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail StartBrowseMdnsEntries() StopBrowseMdnsEntries() } - -/* Hub */ - -// interface for reporting data from connectionsHub to the Service -type ServiceProvider interface { - // report a newly discovered remote EEBUS service - VisibleMDNSRecordsUpdated(entries []*shipapi.MdnsEntry) - - // report a connection to a SKI - RemoteSKIConnected(ski string) - - // report a disconnection to a SKI - RemoteSKIDisconnected(ski string) - - // provide the SHIP ID received during SHIP handshake process - // the ID needs to be stored and then provided for remote services so it can be compared and verified - ServiceShipIDUpdate(ski string, shipID string) - - // provides the current handshake state for a given SKI - ServicePairingDetailUpdate(ski string, detail *ConnectionStateDetail) - - // return if the user is still able to trust the connection - AllowWaitingForTrust(ski string) bool -} - -type ConnectionsHub interface { - PairingDetailForSki(ski string) *ConnectionStateDetail - StartBrowseMdnsSearch() - StopBrowseMdnsSearch() - Start() - Shutdown() - ServiceForSKI(ski string) *ServiceDetails - RegisterRemoteSKI(ski string, enable bool) - InitiatePairingWithSKI(ski string) - CancelPairingWithSKI(ski string) - DisconnectSKI(ski string, reason string) -} diff --git a/api/connectionstate.go b/api/connectionstate.go deleted file mode 100644 index 4421272a..00000000 --- a/api/connectionstate.go +++ /dev/null @@ -1,80 +0,0 @@ -package api - -import ( - "errors" - "sync" -) - -// connection state for global usage, e.g. UI -type ConnectionState uint - -const ( - ConnectionStateNone ConnectionState = iota // The initial state, when no pairing exists - ConnectionStateQueued // The connection request has been started and is pending connection initialization - ConnectionStateInitiated // This service initiated the connection process - ConnectionStateReceivedPairingRequest // A remote service initiated the connection process - ConnectionStateInProgress // The connection handshake is in progress - ConnectionStateTrusted // The connection is trusted on both ends - ConnectionStatePin // PIN processing, not supported right now! - ConnectionStateCompleted // The connection handshake is completed from both ends - ConnectionStateRemoteDeniedTrust // The remote service denied trust - ConnectionStateError // The connection handshake resulted in an error -) - -// the connection state of a service and error if applicable -type ConnectionStateDetail struct { - state ConnectionState - error error - - mux sync.Mutex -} - -func NewConnectionStateDetail(state ConnectionState, err error) *ConnectionStateDetail { - return &ConnectionStateDetail{ - state: state, - error: err, - } -} - -func (c *ConnectionStateDetail) State() ConnectionState { - c.mux.Lock() - defer c.mux.Unlock() - - return c.state -} - -func (c *ConnectionStateDetail) SetState(state ConnectionState) { - c.mux.Lock() - defer c.mux.Unlock() - - c.state = state -} - -func (c *ConnectionStateDetail) Error() error { - c.mux.Lock() - defer c.mux.Unlock() - - return c.error -} - -func (c *ConnectionStateDetail) SetError(err error) { - c.mux.Lock() - defer c.mux.Unlock() - - c.error = err -} - -// ErrServiceNotPaired if the given SKI is not paired yet -var ErrServiceNotPaired = errors.New("the provided SKI is not paired") - -// ErrConnectionNotFound that there was no active connection for a given SKI found -var ErrConnectionNotFound = errors.New("no connection for provided SKI found") - -type RemoteService struct { - Name string `json:"name"` - Ski string `json:"ski"` - Identifier string `json:"identifier"` - Brand string `json:"brand"` - Type string `json:"type"` - Model string `json:"model"` -} diff --git a/api/connectionstate_test.go b/api/connectionstate_test.go deleted file mode 100644 index 583adb23..00000000 --- a/api/connectionstate_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package api - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestTypesSuite(t *testing.T) { - suite.Run(t, new(TypesSuite)) -} - -type TypesSuite struct { - suite.Suite -} - -func (s *TypesSuite) Test_ConnectionState() { - conState := NewConnectionStateDetail(ConnectionStateNone, nil) - assert.Equal(s.T(), ConnectionStateNone, conState.State()) - assert.Nil(s.T(), conState.Error()) - - conState.SetState(ConnectionStateError) - assert.Equal(s.T(), ConnectionStateError, conState.State()) - - conState.SetError(errors.New("test")) - assert.NotNil(s.T(), conState.Error()) -} diff --git a/api/servicedetails.go b/api/servicedetails.go deleted file mode 100644 index 86bc9fb9..00000000 --- a/api/servicedetails.go +++ /dev/null @@ -1,67 +0,0 @@ -package api - -import ( - "sync" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/model" -) - -// generic service details about the local or any remote service -type ServiceDetails struct { - // This is the SKI of the service - // This needs to be persisted - SKI string - - // This is the IPv4 address of the device running the service - // This is optional only needed when this runs with - // zeroconf as mDNS and the remote device is using the latest - // avahi version and thus zeroconf can sometimes not detect - // the IPv4 address and not initiate a connection - IPv4 string - - // shipID is the SHIP identifier of the service - // This needs to be persisted - ShipID string - - // The EEBUS device type of the device model - DeviceType model.DeviceTypeType - - // Flags if the service auto auto accepts other services - RegisterAutoAccept bool - - // Flags if the service is trusted and should be reconnected to - // Should be enabled after the connection process resulted - // ConnectionStateDetail == ConnectionStateTrusted the first time - Trusted bool - - // the current connection state details - connectionStateDetail *ConnectionStateDetail - - mux sync.Mutex -} - -// create a new ServiceDetails record with a SKI -func NewServiceDetails(ski string) *ServiceDetails { - connState := NewConnectionStateDetail(ConnectionStateNone, nil) - service := &ServiceDetails{ - SKI: util.NormalizeSKI(ski), // standardize the provided SKI strings - connectionStateDetail: connState, - } - - return service -} - -func (s *ServiceDetails) ConnectionStateDetail() *ConnectionStateDetail { - s.mux.Lock() - defer s.mux.Unlock() - - return s.connectionStateDetail -} - -func (s *ServiceDetails) SetConnectionStateDetail(detail *ConnectionStateDetail) { - s.mux.Lock() - defer s.mux.Unlock() - - s.connectionStateDetail = detail -} diff --git a/api/servicedetails_test.go b/api/servicedetails_test.go deleted file mode 100644 index a84f448b..00000000 --- a/api/servicedetails_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package api - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -func TestServiceDetails(t *testing.T) { - suite.Run(t, new(ServiceDetailsSuite)) -} - -type ServiceDetailsSuite struct { - suite.Suite -} - -func (s *ServiceDetailsSuite) Test_ServiceDetails() { - testSki := "test" - - details := NewServiceDetails(testSki) - assert.NotNil(s.T(), details) - - conState := NewConnectionStateDetail(ConnectionStateNone, nil) - details.SetConnectionStateDetail(conState) - - state := details.ConnectionStateDetail() - assert.Equal(s.T(), ConnectionStateNone, state.State()) -} diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 5863df4a..44d583e5 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -15,6 +15,7 @@ import ( "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/service" + shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/cert" "github.com/enbility/spine-go/model" ) @@ -97,13 +98,13 @@ func (h *evse) RemoteSKIConnected(service api.EEBUSService, ski string) {} func (h *evse) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} -func (h *evse) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []api.RemoteService) { +func (h *evse) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []shipapi.RemoteService) { } func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *evse) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { - if ski == remoteSki && detail.State() == api.ConnectionStateRemoteDeniedTrust { +func (h *evse) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { + if ski == remoteSki && detail.State() == shipapi.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") h.myService.RegisterRemoteSKI(ski, false) h.myService.CancelPairingWithSKI(ski) diff --git a/cmd/hems/main.go b/cmd/hems/main.go index 93d6afb4..0961f993 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -15,6 +15,7 @@ import ( "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/service" + shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/cert" "github.com/enbility/spine-go/model" ) @@ -97,13 +98,13 @@ func (h *hems) RemoteSKIConnected(service api.EEBUSService, ski string) {} func (h *hems) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} -func (h *hems) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []api.RemoteService) { +func (h *hems) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []shipapi.RemoteService) { } func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *hems) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { - if ski == remoteSki && detail.State() == api.ConnectionStateRemoteDeniedTrust { +func (h *hems) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { + if ski == remoteSki && detail.State() == shipapi.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") h.myService.RegisterRemoteSKI(ski, false) h.myService.CancelPairingWithSKI(ski) diff --git a/go.mod b/go.mod index ea04359b..8081d056 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,9 @@ module github.com/enbility/eebus-go go 1.18 require ( - github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c + github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4 github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c - github.com/gorilla/websocket v1.5.1 github.com/stretchr/testify v1.8.4 - go.uber.org/mock v0.4.0 ) require ( @@ -15,6 +13,7 @@ require ( github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed // indirect github.com/miekg/dns v1.1.57 // indirect @@ -23,6 +22,7 @@ require ( github.com/rickb777/plural v1.4.1 // indirect github.com/stretchr/objx v0.5.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect + go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.20.0 // indirect golang.org/x/sys v0.16.0 // indirect diff --git a/go.sum b/go.sum index cf25f9aa..a9b63445 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c h1:o9u0pYg0iyVW+iLtJZ5Xpico3bfv71zNowPARy3CZDY= -github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= +github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4 h1:udB9WMUnI/YvoW8armfvylz0iP4ISjWdWbgI7yRO+Jk= +github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/mocks/ConnectionsHub.go b/mocks/ConnectionsHub.go deleted file mode 100644 index b65cbf9c..00000000 --- a/mocks/ConnectionsHub.go +++ /dev/null @@ -1,393 +0,0 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. - -package mocks - -import ( - api "github.com/enbility/eebus-go/api" - mock "github.com/stretchr/testify/mock" -) - -// ConnectionsHub is an autogenerated mock type for the ConnectionsHub type -type ConnectionsHub struct { - mock.Mock -} - -type ConnectionsHub_Expecter struct { - mock *mock.Mock -} - -func (_m *ConnectionsHub) EXPECT() *ConnectionsHub_Expecter { - return &ConnectionsHub_Expecter{mock: &_m.Mock} -} - -// CancelPairingWithSKI provides a mock function with given fields: ski -func (_m *ConnectionsHub) CancelPairingWithSKI(ski string) { - _m.Called(ski) -} - -// ConnectionsHub_CancelPairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelPairingWithSKI' -type ConnectionsHub_CancelPairingWithSKI_Call struct { - *mock.Call -} - -// CancelPairingWithSKI is a helper method to define mock.On call -// - ski string -func (_e *ConnectionsHub_Expecter) CancelPairingWithSKI(ski interface{}) *ConnectionsHub_CancelPairingWithSKI_Call { - return &ConnectionsHub_CancelPairingWithSKI_Call{Call: _e.mock.On("CancelPairingWithSKI", ski)} -} - -func (_c *ConnectionsHub_CancelPairingWithSKI_Call) Run(run func(ski string)) *ConnectionsHub_CancelPairingWithSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ConnectionsHub_CancelPairingWithSKI_Call) Return() *ConnectionsHub_CancelPairingWithSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *ConnectionsHub_CancelPairingWithSKI_Call) RunAndReturn(run func(string)) *ConnectionsHub_CancelPairingWithSKI_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectSKI provides a mock function with given fields: ski, reason -func (_m *ConnectionsHub) DisconnectSKI(ski string, reason string) { - _m.Called(ski, reason) -} - -// ConnectionsHub_DisconnectSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectSKI' -type ConnectionsHub_DisconnectSKI_Call struct { - *mock.Call -} - -// DisconnectSKI is a helper method to define mock.On call -// - ski string -// - reason string -func (_e *ConnectionsHub_Expecter) DisconnectSKI(ski interface{}, reason interface{}) *ConnectionsHub_DisconnectSKI_Call { - return &ConnectionsHub_DisconnectSKI_Call{Call: _e.mock.On("DisconnectSKI", ski, reason)} -} - -func (_c *ConnectionsHub_DisconnectSKI_Call) Run(run func(ski string, reason string)) *ConnectionsHub_DisconnectSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *ConnectionsHub_DisconnectSKI_Call) Return() *ConnectionsHub_DisconnectSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *ConnectionsHub_DisconnectSKI_Call) RunAndReturn(run func(string, string)) *ConnectionsHub_DisconnectSKI_Call { - _c.Call.Return(run) - return _c -} - -// InitiatePairingWithSKI provides a mock function with given fields: ski -func (_m *ConnectionsHub) InitiatePairingWithSKI(ski string) { - _m.Called(ski) -} - -// ConnectionsHub_InitiatePairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitiatePairingWithSKI' -type ConnectionsHub_InitiatePairingWithSKI_Call struct { - *mock.Call -} - -// InitiatePairingWithSKI is a helper method to define mock.On call -// - ski string -func (_e *ConnectionsHub_Expecter) InitiatePairingWithSKI(ski interface{}) *ConnectionsHub_InitiatePairingWithSKI_Call { - return &ConnectionsHub_InitiatePairingWithSKI_Call{Call: _e.mock.On("InitiatePairingWithSKI", ski)} -} - -func (_c *ConnectionsHub_InitiatePairingWithSKI_Call) Run(run func(ski string)) *ConnectionsHub_InitiatePairingWithSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ConnectionsHub_InitiatePairingWithSKI_Call) Return() *ConnectionsHub_InitiatePairingWithSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *ConnectionsHub_InitiatePairingWithSKI_Call) RunAndReturn(run func(string)) *ConnectionsHub_InitiatePairingWithSKI_Call { - _c.Call.Return(run) - return _c -} - -// PairingDetailForSki provides a mock function with given fields: ski -func (_m *ConnectionsHub) PairingDetailForSki(ski string) *api.ConnectionStateDetail { - ret := _m.Called(ski) - - if len(ret) == 0 { - panic("no return value specified for PairingDetailForSki") - } - - var r0 *api.ConnectionStateDetail - if rf, ok := ret.Get(0).(func(string) *api.ConnectionStateDetail); ok { - r0 = rf(ski) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*api.ConnectionStateDetail) - } - } - - return r0 -} - -// ConnectionsHub_PairingDetailForSki_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PairingDetailForSki' -type ConnectionsHub_PairingDetailForSki_Call struct { - *mock.Call -} - -// PairingDetailForSki is a helper method to define mock.On call -// - ski string -func (_e *ConnectionsHub_Expecter) PairingDetailForSki(ski interface{}) *ConnectionsHub_PairingDetailForSki_Call { - return &ConnectionsHub_PairingDetailForSki_Call{Call: _e.mock.On("PairingDetailForSki", ski)} -} - -func (_c *ConnectionsHub_PairingDetailForSki_Call) Run(run func(ski string)) *ConnectionsHub_PairingDetailForSki_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ConnectionsHub_PairingDetailForSki_Call) Return(_a0 *api.ConnectionStateDetail) *ConnectionsHub_PairingDetailForSki_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *ConnectionsHub_PairingDetailForSki_Call) RunAndReturn(run func(string) *api.ConnectionStateDetail) *ConnectionsHub_PairingDetailForSki_Call { - _c.Call.Return(run) - return _c -} - -// RegisterRemoteSKI provides a mock function with given fields: ski, enable -func (_m *ConnectionsHub) RegisterRemoteSKI(ski string, enable bool) { - _m.Called(ski, enable) -} - -// ConnectionsHub_RegisterRemoteSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterRemoteSKI' -type ConnectionsHub_RegisterRemoteSKI_Call struct { - *mock.Call -} - -// RegisterRemoteSKI is a helper method to define mock.On call -// - ski string -// - enable bool -func (_e *ConnectionsHub_Expecter) RegisterRemoteSKI(ski interface{}, enable interface{}) *ConnectionsHub_RegisterRemoteSKI_Call { - return &ConnectionsHub_RegisterRemoteSKI_Call{Call: _e.mock.On("RegisterRemoteSKI", ski, enable)} -} - -func (_c *ConnectionsHub_RegisterRemoteSKI_Call) Run(run func(ski string, enable bool)) *ConnectionsHub_RegisterRemoteSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *ConnectionsHub_RegisterRemoteSKI_Call) Return() *ConnectionsHub_RegisterRemoteSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *ConnectionsHub_RegisterRemoteSKI_Call) RunAndReturn(run func(string, bool)) *ConnectionsHub_RegisterRemoteSKI_Call { - _c.Call.Return(run) - return _c -} - -// ServiceForSKI provides a mock function with given fields: ski -func (_m *ConnectionsHub) ServiceForSKI(ski string) *api.ServiceDetails { - ret := _m.Called(ski) - - if len(ret) == 0 { - panic("no return value specified for ServiceForSKI") - } - - var r0 *api.ServiceDetails - if rf, ok := ret.Get(0).(func(string) *api.ServiceDetails); ok { - r0 = rf(ski) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*api.ServiceDetails) - } - } - - return r0 -} - -// ConnectionsHub_ServiceForSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServiceForSKI' -type ConnectionsHub_ServiceForSKI_Call struct { - *mock.Call -} - -// ServiceForSKI is a helper method to define mock.On call -// - ski string -func (_e *ConnectionsHub_Expecter) ServiceForSKI(ski interface{}) *ConnectionsHub_ServiceForSKI_Call { - return &ConnectionsHub_ServiceForSKI_Call{Call: _e.mock.On("ServiceForSKI", ski)} -} - -func (_c *ConnectionsHub_ServiceForSKI_Call) Run(run func(ski string)) *ConnectionsHub_ServiceForSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ConnectionsHub_ServiceForSKI_Call) Return(_a0 *api.ServiceDetails) *ConnectionsHub_ServiceForSKI_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *ConnectionsHub_ServiceForSKI_Call) RunAndReturn(run func(string) *api.ServiceDetails) *ConnectionsHub_ServiceForSKI_Call { - _c.Call.Return(run) - return _c -} - -// Shutdown provides a mock function with given fields: -func (_m *ConnectionsHub) Shutdown() { - _m.Called() -} - -// ConnectionsHub_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown' -type ConnectionsHub_Shutdown_Call struct { - *mock.Call -} - -// Shutdown is a helper method to define mock.On call -func (_e *ConnectionsHub_Expecter) Shutdown() *ConnectionsHub_Shutdown_Call { - return &ConnectionsHub_Shutdown_Call{Call: _e.mock.On("Shutdown")} -} - -func (_c *ConnectionsHub_Shutdown_Call) Run(run func()) *ConnectionsHub_Shutdown_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *ConnectionsHub_Shutdown_Call) Return() *ConnectionsHub_Shutdown_Call { - _c.Call.Return() - return _c -} - -func (_c *ConnectionsHub_Shutdown_Call) RunAndReturn(run func()) *ConnectionsHub_Shutdown_Call { - _c.Call.Return(run) - return _c -} - -// Start provides a mock function with given fields: -func (_m *ConnectionsHub) Start() { - _m.Called() -} - -// ConnectionsHub_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type ConnectionsHub_Start_Call struct { - *mock.Call -} - -// Start is a helper method to define mock.On call -func (_e *ConnectionsHub_Expecter) Start() *ConnectionsHub_Start_Call { - return &ConnectionsHub_Start_Call{Call: _e.mock.On("Start")} -} - -func (_c *ConnectionsHub_Start_Call) Run(run func()) *ConnectionsHub_Start_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *ConnectionsHub_Start_Call) Return() *ConnectionsHub_Start_Call { - _c.Call.Return() - return _c -} - -func (_c *ConnectionsHub_Start_Call) RunAndReturn(run func()) *ConnectionsHub_Start_Call { - _c.Call.Return(run) - return _c -} - -// StartBrowseMdnsSearch provides a mock function with given fields: -func (_m *ConnectionsHub) StartBrowseMdnsSearch() { - _m.Called() -} - -// ConnectionsHub_StartBrowseMdnsSearch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartBrowseMdnsSearch' -type ConnectionsHub_StartBrowseMdnsSearch_Call struct { - *mock.Call -} - -// StartBrowseMdnsSearch is a helper method to define mock.On call -func (_e *ConnectionsHub_Expecter) StartBrowseMdnsSearch() *ConnectionsHub_StartBrowseMdnsSearch_Call { - return &ConnectionsHub_StartBrowseMdnsSearch_Call{Call: _e.mock.On("StartBrowseMdnsSearch")} -} - -func (_c *ConnectionsHub_StartBrowseMdnsSearch_Call) Run(run func()) *ConnectionsHub_StartBrowseMdnsSearch_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *ConnectionsHub_StartBrowseMdnsSearch_Call) Return() *ConnectionsHub_StartBrowseMdnsSearch_Call { - _c.Call.Return() - return _c -} - -func (_c *ConnectionsHub_StartBrowseMdnsSearch_Call) RunAndReturn(run func()) *ConnectionsHub_StartBrowseMdnsSearch_Call { - _c.Call.Return(run) - return _c -} - -// StopBrowseMdnsSearch provides a mock function with given fields: -func (_m *ConnectionsHub) StopBrowseMdnsSearch() { - _m.Called() -} - -// ConnectionsHub_StopBrowseMdnsSearch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StopBrowseMdnsSearch' -type ConnectionsHub_StopBrowseMdnsSearch_Call struct { - *mock.Call -} - -// StopBrowseMdnsSearch is a helper method to define mock.On call -func (_e *ConnectionsHub_Expecter) StopBrowseMdnsSearch() *ConnectionsHub_StopBrowseMdnsSearch_Call { - return &ConnectionsHub_StopBrowseMdnsSearch_Call{Call: _e.mock.On("StopBrowseMdnsSearch")} -} - -func (_c *ConnectionsHub_StopBrowseMdnsSearch_Call) Run(run func()) *ConnectionsHub_StopBrowseMdnsSearch_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *ConnectionsHub_StopBrowseMdnsSearch_Call) Return() *ConnectionsHub_StopBrowseMdnsSearch_Call { - _c.Call.Return() - return _c -} - -func (_c *ConnectionsHub_StopBrowseMdnsSearch_Call) RunAndReturn(run func()) *ConnectionsHub_StopBrowseMdnsSearch_Call { - _c.Call.Return(run) - return _c -} - -// NewConnectionsHub creates a new instance of ConnectionsHub. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewConnectionsHub(t interface { - mock.TestingT - Cleanup(func()) -}) *ConnectionsHub { - mock := &ConnectionsHub{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/mocks/EEBUSService.go b/mocks/EEBUSService.go index ef22e9b1..d7a2293c 100644 --- a/mocks/EEBUSService.go +++ b/mocks/EEBUSService.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" + ship_goapi "github.com/enbility/ship-go/api" + spine_goapi "github.com/enbility/spine-go/api" ) @@ -219,19 +221,19 @@ func (_c *EEBUSService_LocalDevice_Call) RunAndReturn(run func() spine_goapi.Dev } // LocalService provides a mock function with given fields: -func (_m *EEBUSService) LocalService() *api.ServiceDetails { +func (_m *EEBUSService) LocalService() *ship_goapi.ServiceDetails { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for LocalService") } - var r0 *api.ServiceDetails - if rf, ok := ret.Get(0).(func() *api.ServiceDetails); ok { + var r0 *ship_goapi.ServiceDetails + if rf, ok := ret.Get(0).(func() *ship_goapi.ServiceDetails); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*api.ServiceDetails) + r0 = ret.Get(0).(*ship_goapi.ServiceDetails) } } @@ -255,30 +257,30 @@ func (_c *EEBUSService_LocalService_Call) Run(run func()) *EEBUSService_LocalSer return _c } -func (_c *EEBUSService_LocalService_Call) Return(_a0 *api.ServiceDetails) *EEBUSService_LocalService_Call { +func (_c *EEBUSService_LocalService_Call) Return(_a0 *ship_goapi.ServiceDetails) *EEBUSService_LocalService_Call { _c.Call.Return(_a0) return _c } -func (_c *EEBUSService_LocalService_Call) RunAndReturn(run func() *api.ServiceDetails) *EEBUSService_LocalService_Call { +func (_c *EEBUSService_LocalService_Call) RunAndReturn(run func() *ship_goapi.ServiceDetails) *EEBUSService_LocalService_Call { _c.Call.Return(run) return _c } // PairingDetailForSki provides a mock function with given fields: ski -func (_m *EEBUSService) PairingDetailForSki(ski string) *api.ConnectionStateDetail { +func (_m *EEBUSService) PairingDetailForSki(ski string) *ship_goapi.ConnectionStateDetail { ret := _m.Called(ski) if len(ret) == 0 { panic("no return value specified for PairingDetailForSki") } - var r0 *api.ConnectionStateDetail - if rf, ok := ret.Get(0).(func(string) *api.ConnectionStateDetail); ok { + var r0 *ship_goapi.ConnectionStateDetail + if rf, ok := ret.Get(0).(func(string) *ship_goapi.ConnectionStateDetail); ok { r0 = rf(ski) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*api.ConnectionStateDetail) + r0 = ret.Get(0).(*ship_goapi.ConnectionStateDetail) } } @@ -303,12 +305,12 @@ func (_c *EEBUSService_PairingDetailForSki_Call) Run(run func(ski string)) *EEBU return _c } -func (_c *EEBUSService_PairingDetailForSki_Call) Return(_a0 *api.ConnectionStateDetail) *EEBUSService_PairingDetailForSki_Call { +func (_c *EEBUSService_PairingDetailForSki_Call) Return(_a0 *ship_goapi.ConnectionStateDetail) *EEBUSService_PairingDetailForSki_Call { _c.Call.Return(_a0) return _c } -func (_c *EEBUSService_PairingDetailForSki_Call) RunAndReturn(run func(string) *api.ConnectionStateDetail) *EEBUSService_PairingDetailForSki_Call { +func (_c *EEBUSService_PairingDetailForSki_Call) RunAndReturn(run func(string) *ship_goapi.ConnectionStateDetail) *EEBUSService_PairingDetailForSki_Call { _c.Call.Return(run) return _c } @@ -348,19 +350,19 @@ func (_c *EEBUSService_RegisterRemoteSKI_Call) RunAndReturn(run func(string, boo } // RemoteServiceForSKI provides a mock function with given fields: ski -func (_m *EEBUSService) RemoteServiceForSKI(ski string) *api.ServiceDetails { +func (_m *EEBUSService) RemoteServiceForSKI(ski string) *ship_goapi.ServiceDetails { ret := _m.Called(ski) if len(ret) == 0 { panic("no return value specified for RemoteServiceForSKI") } - var r0 *api.ServiceDetails - if rf, ok := ret.Get(0).(func(string) *api.ServiceDetails); ok { + var r0 *ship_goapi.ServiceDetails + if rf, ok := ret.Get(0).(func(string) *ship_goapi.ServiceDetails); ok { r0 = rf(ski) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*api.ServiceDetails) + r0 = ret.Get(0).(*ship_goapi.ServiceDetails) } } @@ -385,12 +387,12 @@ func (_c *EEBUSService_RemoteServiceForSKI_Call) Run(run func(ski string)) *EEBU return _c } -func (_c *EEBUSService_RemoteServiceForSKI_Call) Return(_a0 *api.ServiceDetails) *EEBUSService_RemoteServiceForSKI_Call { +func (_c *EEBUSService_RemoteServiceForSKI_Call) Return(_a0 *ship_goapi.ServiceDetails) *EEBUSService_RemoteServiceForSKI_Call { _c.Call.Return(_a0) return _c } -func (_c *EEBUSService_RemoteServiceForSKI_Call) RunAndReturn(run func(string) *api.ServiceDetails) *EEBUSService_RemoteServiceForSKI_Call { +func (_c *EEBUSService_RemoteServiceForSKI_Call) RunAndReturn(run func(string) *ship_goapi.ServiceDetails) *EEBUSService_RemoteServiceForSKI_Call { _c.Call.Return(run) return _c } diff --git a/mocks/EEBUSServiceHandler.go b/mocks/EEBUSServiceHandler.go index 35b6f231..ff2872f8 100644 --- a/mocks/EEBUSServiceHandler.go +++ b/mocks/EEBUSServiceHandler.go @@ -5,6 +5,8 @@ package mocks import ( api "github.com/enbility/eebus-go/api" mock "github.com/stretchr/testify/mock" + + ship_goapi "github.com/enbility/ship-go/api" ) // EEBUSServiceHandler is an autogenerated mock type for the EEBUSServiceHandler type @@ -135,7 +137,7 @@ func (_c *EEBUSServiceHandler_RemoteSKIDisconnected_Call) RunAndReturn(run func( } // ServicePairingDetailUpdate provides a mock function with given fields: ski, detail -func (_m *EEBUSServiceHandler) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { +func (_m *EEBUSServiceHandler) ServicePairingDetailUpdate(ski string, detail *ship_goapi.ConnectionStateDetail) { _m.Called(ski, detail) } @@ -146,14 +148,14 @@ type EEBUSServiceHandler_ServicePairingDetailUpdate_Call struct { // ServicePairingDetailUpdate is a helper method to define mock.On call // - ski string -// - detail *api.ConnectionStateDetail +// - detail *ship_goapi.ConnectionStateDetail func (_e *EEBUSServiceHandler_Expecter) ServicePairingDetailUpdate(ski interface{}, detail interface{}) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { return &EEBUSServiceHandler_ServicePairingDetailUpdate_Call{Call: _e.mock.On("ServicePairingDetailUpdate", ski, detail)} } -func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) Run(run func(ski string, detail *api.ConnectionStateDetail)) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { +func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) Run(run func(ski string, detail *ship_goapi.ConnectionStateDetail)) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(*api.ConnectionStateDetail)) + run(args[0].(string), args[1].(*ship_goapi.ConnectionStateDetail)) }) return _c } @@ -163,7 +165,7 @@ func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) Return() *EEBUSSe return _c } -func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) RunAndReturn(run func(string, *api.ConnectionStateDetail)) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { +func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) RunAndReturn(run func(string, *ship_goapi.ConnectionStateDetail)) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { _c.Call.Return(run) return _c } @@ -203,7 +205,7 @@ func (_c *EEBUSServiceHandler_ServiceShipIDUpdate_Call) RunAndReturn(run func(st } // VisibleRemoteServicesUpdated provides a mock function with given fields: service, entries -func (_m *EEBUSServiceHandler) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []api.RemoteService) { +func (_m *EEBUSServiceHandler) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []ship_goapi.RemoteService) { _m.Called(service, entries) } @@ -214,14 +216,14 @@ type EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call struct { // VisibleRemoteServicesUpdated is a helper method to define mock.On call // - service api.EEBUSService -// - entries []api.RemoteService +// - entries []ship_goapi.RemoteService func (_e *EEBUSServiceHandler_Expecter) VisibleRemoteServicesUpdated(service interface{}, entries interface{}) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { return &EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call{Call: _e.mock.On("VisibleRemoteServicesUpdated", service, entries)} } -func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) Run(run func(service api.EEBUSService, entries []api.RemoteService)) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { +func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) Run(run func(service api.EEBUSService, entries []ship_goapi.RemoteService)) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.EEBUSService), args[1].([]api.RemoteService)) + run(args[0].(api.EEBUSService), args[1].([]ship_goapi.RemoteService)) }) return _c } @@ -231,7 +233,7 @@ func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) Return() *EEBUS return _c } -func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) RunAndReturn(run func(api.EEBUSService, []api.RemoteService)) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { +func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) RunAndReturn(run func(api.EEBUSService, []ship_goapi.RemoteService)) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { _c.Call.Return(run) return _c } diff --git a/mocks/ServiceProvider.go b/mocks/ServiceProvider.go deleted file mode 100644 index 2d0e4759..00000000 --- a/mocks/ServiceProvider.go +++ /dev/null @@ -1,250 +0,0 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. - -package mocks - -import ( - api "github.com/enbility/eebus-go/api" - mock "github.com/stretchr/testify/mock" - - ship_goapi "github.com/enbility/ship-go/api" -) - -// ServiceProvider is an autogenerated mock type for the ServiceProvider type -type ServiceProvider struct { - mock.Mock -} - -type ServiceProvider_Expecter struct { - mock *mock.Mock -} - -func (_m *ServiceProvider) EXPECT() *ServiceProvider_Expecter { - return &ServiceProvider_Expecter{mock: &_m.Mock} -} - -// AllowWaitingForTrust provides a mock function with given fields: ski -func (_m *ServiceProvider) AllowWaitingForTrust(ski string) bool { - ret := _m.Called(ski) - - if len(ret) == 0 { - panic("no return value specified for AllowWaitingForTrust") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(string) bool); ok { - r0 = rf(ski) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// ServiceProvider_AllowWaitingForTrust_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AllowWaitingForTrust' -type ServiceProvider_AllowWaitingForTrust_Call struct { - *mock.Call -} - -// AllowWaitingForTrust is a helper method to define mock.On call -// - ski string -func (_e *ServiceProvider_Expecter) AllowWaitingForTrust(ski interface{}) *ServiceProvider_AllowWaitingForTrust_Call { - return &ServiceProvider_AllowWaitingForTrust_Call{Call: _e.mock.On("AllowWaitingForTrust", ski)} -} - -func (_c *ServiceProvider_AllowWaitingForTrust_Call) Run(run func(ski string)) *ServiceProvider_AllowWaitingForTrust_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ServiceProvider_AllowWaitingForTrust_Call) Return(_a0 bool) *ServiceProvider_AllowWaitingForTrust_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *ServiceProvider_AllowWaitingForTrust_Call) RunAndReturn(run func(string) bool) *ServiceProvider_AllowWaitingForTrust_Call { - _c.Call.Return(run) - return _c -} - -// RemoteSKIConnected provides a mock function with given fields: ski -func (_m *ServiceProvider) RemoteSKIConnected(ski string) { - _m.Called(ski) -} - -// ServiceProvider_RemoteSKIConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIConnected' -type ServiceProvider_RemoteSKIConnected_Call struct { - *mock.Call -} - -// RemoteSKIConnected is a helper method to define mock.On call -// - ski string -func (_e *ServiceProvider_Expecter) RemoteSKIConnected(ski interface{}) *ServiceProvider_RemoteSKIConnected_Call { - return &ServiceProvider_RemoteSKIConnected_Call{Call: _e.mock.On("RemoteSKIConnected", ski)} -} - -func (_c *ServiceProvider_RemoteSKIConnected_Call) Run(run func(ski string)) *ServiceProvider_RemoteSKIConnected_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ServiceProvider_RemoteSKIConnected_Call) Return() *ServiceProvider_RemoteSKIConnected_Call { - _c.Call.Return() - return _c -} - -func (_c *ServiceProvider_RemoteSKIConnected_Call) RunAndReturn(run func(string)) *ServiceProvider_RemoteSKIConnected_Call { - _c.Call.Return(run) - return _c -} - -// RemoteSKIDisconnected provides a mock function with given fields: ski -func (_m *ServiceProvider) RemoteSKIDisconnected(ski string) { - _m.Called(ski) -} - -// ServiceProvider_RemoteSKIDisconnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIDisconnected' -type ServiceProvider_RemoteSKIDisconnected_Call struct { - *mock.Call -} - -// RemoteSKIDisconnected is a helper method to define mock.On call -// - ski string -func (_e *ServiceProvider_Expecter) RemoteSKIDisconnected(ski interface{}) *ServiceProvider_RemoteSKIDisconnected_Call { - return &ServiceProvider_RemoteSKIDisconnected_Call{Call: _e.mock.On("RemoteSKIDisconnected", ski)} -} - -func (_c *ServiceProvider_RemoteSKIDisconnected_Call) Run(run func(ski string)) *ServiceProvider_RemoteSKIDisconnected_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ServiceProvider_RemoteSKIDisconnected_Call) Return() *ServiceProvider_RemoteSKIDisconnected_Call { - _c.Call.Return() - return _c -} - -func (_c *ServiceProvider_RemoteSKIDisconnected_Call) RunAndReturn(run func(string)) *ServiceProvider_RemoteSKIDisconnected_Call { - _c.Call.Return(run) - return _c -} - -// ServicePairingDetailUpdate provides a mock function with given fields: ski, detail -func (_m *ServiceProvider) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { - _m.Called(ski, detail) -} - -// ServiceProvider_ServicePairingDetailUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServicePairingDetailUpdate' -type ServiceProvider_ServicePairingDetailUpdate_Call struct { - *mock.Call -} - -// ServicePairingDetailUpdate is a helper method to define mock.On call -// - ski string -// - detail *api.ConnectionStateDetail -func (_e *ServiceProvider_Expecter) ServicePairingDetailUpdate(ski interface{}, detail interface{}) *ServiceProvider_ServicePairingDetailUpdate_Call { - return &ServiceProvider_ServicePairingDetailUpdate_Call{Call: _e.mock.On("ServicePairingDetailUpdate", ski, detail)} -} - -func (_c *ServiceProvider_ServicePairingDetailUpdate_Call) Run(run func(ski string, detail *api.ConnectionStateDetail)) *ServiceProvider_ServicePairingDetailUpdate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(*api.ConnectionStateDetail)) - }) - return _c -} - -func (_c *ServiceProvider_ServicePairingDetailUpdate_Call) Return() *ServiceProvider_ServicePairingDetailUpdate_Call { - _c.Call.Return() - return _c -} - -func (_c *ServiceProvider_ServicePairingDetailUpdate_Call) RunAndReturn(run func(string, *api.ConnectionStateDetail)) *ServiceProvider_ServicePairingDetailUpdate_Call { - _c.Call.Return(run) - return _c -} - -// ServiceShipIDUpdate provides a mock function with given fields: ski, shipID -func (_m *ServiceProvider) ServiceShipIDUpdate(ski string, shipID string) { - _m.Called(ski, shipID) -} - -// ServiceProvider_ServiceShipIDUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServiceShipIDUpdate' -type ServiceProvider_ServiceShipIDUpdate_Call struct { - *mock.Call -} - -// ServiceShipIDUpdate is a helper method to define mock.On call -// - ski string -// - shipID string -func (_e *ServiceProvider_Expecter) ServiceShipIDUpdate(ski interface{}, shipID interface{}) *ServiceProvider_ServiceShipIDUpdate_Call { - return &ServiceProvider_ServiceShipIDUpdate_Call{Call: _e.mock.On("ServiceShipIDUpdate", ski, shipID)} -} - -func (_c *ServiceProvider_ServiceShipIDUpdate_Call) Run(run func(ski string, shipID string)) *ServiceProvider_ServiceShipIDUpdate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *ServiceProvider_ServiceShipIDUpdate_Call) Return() *ServiceProvider_ServiceShipIDUpdate_Call { - _c.Call.Return() - return _c -} - -func (_c *ServiceProvider_ServiceShipIDUpdate_Call) RunAndReturn(run func(string, string)) *ServiceProvider_ServiceShipIDUpdate_Call { - _c.Call.Return(run) - return _c -} - -// VisibleMDNSRecordsUpdated provides a mock function with given fields: entries -func (_m *ServiceProvider) VisibleMDNSRecordsUpdated(entries []*ship_goapi.MdnsEntry) { - _m.Called(entries) -} - -// ServiceProvider_VisibleMDNSRecordsUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VisibleMDNSRecordsUpdated' -type ServiceProvider_VisibleMDNSRecordsUpdated_Call struct { - *mock.Call -} - -// VisibleMDNSRecordsUpdated is a helper method to define mock.On call -// - entries []*ship_goapi.MdnsEntry -func (_e *ServiceProvider_Expecter) VisibleMDNSRecordsUpdated(entries interface{}) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { - return &ServiceProvider_VisibleMDNSRecordsUpdated_Call{Call: _e.mock.On("VisibleMDNSRecordsUpdated", entries)} -} - -func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) Run(run func(entries []*ship_goapi.MdnsEntry)) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]*ship_goapi.MdnsEntry)) - }) - return _c -} - -func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) Return() *ServiceProvider_VisibleMDNSRecordsUpdated_Call { - _c.Call.Return() - return _c -} - -func (_c *ServiceProvider_VisibleMDNSRecordsUpdated_Call) RunAndReturn(run func([]*ship_goapi.MdnsEntry)) *ServiceProvider_VisibleMDNSRecordsUpdated_Call { - _c.Call.Return(run) - return _c -} - -// NewServiceProvider creates a new instance of ServiceProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewServiceProvider(t interface { - mock.TestingT - Cleanup(func()) -}) *ServiceProvider { - mock := &ServiceProvider{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/mocks/mockgen_api.go b/mocks/mockgen_api.go deleted file mode 100644 index 3100eb65..00000000 --- a/mocks/mockgen_api.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/enbility/eebus-go/api (interfaces: ServiceProvider) -// -// Generated by this command: -// -// mockgen -destination=../mocks/mockgen_api.go -package=mocks github.com/enbility/eebus-go/api ServiceProvider -// - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - api "github.com/enbility/eebus-go/api" - api0 "github.com/enbility/ship-go/api" - gomock "go.uber.org/mock/gomock" -) - -// MockServiceProvider is a mock of ServiceProvider interface. -type MockServiceProvider struct { - ctrl *gomock.Controller - recorder *MockServiceProviderMockRecorder -} - -// MockServiceProviderMockRecorder is the mock recorder for MockServiceProvider. -type MockServiceProviderMockRecorder struct { - mock *MockServiceProvider -} - -// NewMockServiceProvider creates a new mock instance. -func NewMockServiceProvider(ctrl *gomock.Controller) *MockServiceProvider { - mock := &MockServiceProvider{ctrl: ctrl} - mock.recorder = &MockServiceProviderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockServiceProvider) EXPECT() *MockServiceProviderMockRecorder { - return m.recorder -} - -// AllowWaitingForTrust mocks base method. -func (m *MockServiceProvider) AllowWaitingForTrust(arg0 string) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AllowWaitingForTrust", arg0) - ret0, _ := ret[0].(bool) - return ret0 -} - -// AllowWaitingForTrust indicates an expected call of AllowWaitingForTrust. -func (mr *MockServiceProviderMockRecorder) AllowWaitingForTrust(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowWaitingForTrust", reflect.TypeOf((*MockServiceProvider)(nil).AllowWaitingForTrust), arg0) -} - -// RemoteSKIConnected mocks base method. -func (m *MockServiceProvider) RemoteSKIConnected(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoteSKIConnected", arg0) -} - -// RemoteSKIConnected indicates an expected call of RemoteSKIConnected. -func (mr *MockServiceProviderMockRecorder) RemoteSKIConnected(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIConnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIConnected), arg0) -} - -// RemoteSKIDisconnected mocks base method. -func (m *MockServiceProvider) RemoteSKIDisconnected(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoteSKIDisconnected", arg0) -} - -// RemoteSKIDisconnected indicates an expected call of RemoteSKIDisconnected. -func (mr *MockServiceProviderMockRecorder) RemoteSKIDisconnected(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteSKIDisconnected", reflect.TypeOf((*MockServiceProvider)(nil).RemoteSKIDisconnected), arg0) -} - -// ServicePairingDetailUpdate mocks base method. -func (m *MockServiceProvider) ServicePairingDetailUpdate(arg0 string, arg1 *api.ConnectionStateDetail) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ServicePairingDetailUpdate", arg0, arg1) -} - -// ServicePairingDetailUpdate indicates an expected call of ServicePairingDetailUpdate. -func (mr *MockServiceProviderMockRecorder) ServicePairingDetailUpdate(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServicePairingDetailUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServicePairingDetailUpdate), arg0, arg1) -} - -// ServiceShipIDUpdate mocks base method. -func (m *MockServiceProvider) ServiceShipIDUpdate(arg0, arg1 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ServiceShipIDUpdate", arg0, arg1) -} - -// ServiceShipIDUpdate indicates an expected call of ServiceShipIDUpdate. -func (mr *MockServiceProviderMockRecorder) ServiceShipIDUpdate(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceShipIDUpdate", reflect.TypeOf((*MockServiceProvider)(nil).ServiceShipIDUpdate), arg0, arg1) -} - -// VisibleMDNSRecordsUpdated mocks base method. -func (m *MockServiceProvider) VisibleMDNSRecordsUpdated(arg0 []*api0.MdnsEntry) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "VisibleMDNSRecordsUpdated", arg0) -} - -// VisibleMDNSRecordsUpdated indicates an expected call of VisibleMDNSRecordsUpdated. -func (mr *MockServiceProviderMockRecorder) VisibleMDNSRecordsUpdated(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisibleMDNSRecordsUpdated", reflect.TypeOf((*MockServiceProvider)(nil).VisibleMDNSRecordsUpdated), arg0) -} diff --git a/service/hub.go b/service/hub.go deleted file mode 100644 index d9d033e1..00000000 --- a/service/hub.go +++ /dev/null @@ -1,884 +0,0 @@ -package service - -import ( - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "math/big" - "math/rand" - "net" - "net/http" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/cert" - "github.com/enbility/ship-go/logging" - shipmodel "github.com/enbility/ship-go/model" - "github.com/enbility/ship-go/ship" - shipws "github.com/enbility/ship-go/ws" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" - "github.com/gorilla/websocket" -) - -// used for randomizing the connection initiation delay -// this limits the possibility of concurrent connection attempts from both sides -type connectionInitiationDelayTimeRange struct { - // defines the minimum and maximum wait time for when to try to initate an connection - min, max int -} - -// defines the delay timeframes in seconds depening on the connection attempt counter -// the last item will be re-used for higher attempt counter values -var connectionInitiationDelayTimeRanges = []connectionInitiationDelayTimeRange{ - {min: 0, max: 3}, - {min: 3, max: 10}, - {min: 10, max: 20}, -} - -// handling all connections to remote services -type connectionsHubImpl struct { - connections map[string]shipapi.ShipConnection - - // which attempt is it to initate an connection to the remote SKI - connectionAttemptCounter map[string]int - connectionAttemptRunning map[string]bool - - configuration *api.Configuration - localService *api.ServiceDetails - - serviceProvider api.ServiceProvider - - // The list of known remote services - remoteServices map[string]*api.ServiceDetails - - // The web server for handling incoming websocket connections - httpServer *http.Server - - // Handling mDNS related tasks - mdns shipapi.MdnsService - - // list of currently known/reported mDNS entries - knownMdnsEntries []*shipapi.MdnsEntry - - spineLocalDevice spineapi.DeviceLocal - - muxCon sync.Mutex - muxConAttempt sync.Mutex - muxReg sync.Mutex - muxMdns sync.Mutex -} - -func newConnectionsHub(serviceProvider api.ServiceProvider, mdns shipapi.MdnsService, spineLocalDevice spineapi.DeviceLocal, configuration *api.Configuration, localService *api.ServiceDetails) api.ConnectionsHub { - hub := &connectionsHubImpl{ - connections: make(map[string]shipapi.ShipConnection), - connectionAttemptCounter: make(map[string]int), - connectionAttemptRunning: make(map[string]bool), - remoteServices: make(map[string]*api.ServiceDetails), - knownMdnsEntries: make([]*shipapi.MdnsEntry, 0), - serviceProvider: serviceProvider, - spineLocalDevice: spineLocalDevice, - configuration: configuration, - localService: localService, - mdns: mdns, - } - - return hub -} - -// Start the ConnectionsHub with all its services -func (h *connectionsHubImpl) Start() { - // start the websocket server - if err := h.startWebsocketServer(); err != nil { - logging.Log().Debug("error during websocket server starting:", err) - } - - // start mDNS - err := h.mdns.SetupMdnsService() - if err != nil { - logging.Log().Debug("error during mdns setup:", err) - } - - h.checkRestartMdnsSearch() -} - -var _ shipapi.ShipServiceDataProvider = (*connectionsHubImpl)(nil) - -// Returns if the provided SKI is from a registered service -func (h *connectionsHubImpl) IsRemoteServiceForSKIPaired(ski string) bool { - service := h.ServiceForSKI(ski) - - return service.Trusted -} - -// The connection was closed, we need to clean up -func (h *connectionsHubImpl) HandleConnectionClosed(connection shipapi.ShipConnection, handshakeCompleted bool) { - remoteSki := connection.RemoteSKI() - if h.spineLocalDevice != nil { - h.spineLocalDevice.RemoveRemoteDeviceConnection(remoteSki) - } - - // only remove this connection if it is the registered one for the ski! - // as we can have double connections but only one can be registered - if existingC := h.connectionForSKI(remoteSki); existingC != nil { - if existingC.DataHandler() == connection.DataHandler() { - h.muxCon.Lock() - delete(h.connections, connection.RemoteSKI()) - h.muxCon.Unlock() - } - - // connection close was after a completed handshake, so we can reset the attetmpt counter - if handshakeCompleted { - h.removeConnectionAttemptCounter(connection.RemoteSKI()) - } - } - - h.serviceProvider.RemoteSKIDisconnected(connection.RemoteSKI()) - - // Do not automatically reconnect if handshake failed and not already paired - remoteService := h.ServiceForSKI(connection.RemoteSKI()) - if !handshakeCompleted && !remoteService.Trusted { - return - } - - h.checkRestartMdnsSearch() -} - -func (h *connectionsHubImpl) SetupRemoteDevice(ski string, writeI shipapi.SpineDataConnection) shipapi.SpineDataProcessing { - return h.spineLocalDevice.SetupRemoteDevice(ski, writeI) -} - -// return the number of paired services -func (h *connectionsHubImpl) numberPairedServices() int { - amount := 0 - - h.muxReg.Lock() - for _, service := range h.remoteServices { - if service.Trusted { - amount++ - } - } - h.muxReg.Unlock() - - return amount -} - -// startup mDNS if a paired service is not connected -func (h *connectionsHubImpl) checkRestartMdnsSearch() { - countPairedServices := h.numberPairedServices() - h.muxCon.Lock() - countConnections := len(h.connections) - h.muxCon.Unlock() - - if countPairedServices > countConnections { - // if this is not a CEM also start the mDNS announcement - if h.localService.DeviceType != model.DeviceTypeTypeEnergyManagementSystem { - _ = h.mdns.AnnounceMdnsEntry() - } - - h.mdns.RegisterMdnsSearch(h) - } -} - -func (h *connectionsHubImpl) StartBrowseMdnsSearch() { - // TODO: this currently collides with searching for a specific SKI - h.mdns.RegisterMdnsSearch(h) -} - -func (h *connectionsHubImpl) StopBrowseMdnsSearch() { - // TODO: this currently collides with searching for a specific SKI - h.mdns.UnregisterMdnsSearch(h) -} - -// Provides the SHIP ID the remote service reported during the handshake process -func (h *connectionsHubImpl) ReportServiceShipID(ski string, shipdID string) { - h.serviceProvider.RemoteSKIConnected(ski) - - h.serviceProvider.ServiceShipIDUpdate(ski, shipdID) -} - -// return if the user is still able to trust the connection -func (h *connectionsHubImpl) AllowWaitingForTrust(ski string) bool { - if service := h.ServiceForSKI(ski); service != nil { - if service.Trusted { - return true - } - } - - return h.serviceProvider.AllowWaitingForTrust(ski) -} - -// Provides the current ship message exchange state for a given SKI and the corresponding error if state is error -func (h *connectionsHubImpl) HandleShipHandshakeStateUpdate(ski string, state shipmodel.ShipState) { - // overwrite service Paired value - if state.State == shipmodel.SmeHelloStateOk { - h.RegisterRemoteSKI(ski, true) - } - - pairingState := h.mapShipMessageExchangeState(state.State, ski) - if state.Error != nil && state.Error != api.ErrConnectionNotFound { - pairingState = api.ConnectionStateError - } - - pairingDetail := api.NewConnectionStateDetail(pairingState, state.Error) - - service := h.ServiceForSKI(ski) - - existingDetails := service.ConnectionStateDetail() - existingState := existingDetails.State() - if existingState != pairingState || existingDetails.Error() != state.Error { - service.SetConnectionStateDetail(pairingDetail) - - h.serviceProvider.ServicePairingDetailUpdate(ski, pairingDetail) - } -} - -// Provide the current pairing state for a given SKI -// -// returns: -// -// ErrNotPaired if the SKI is not in the (to be) paired list -// ErrNoConnectionFound if no connection for the SKI was found -func (h *connectionsHubImpl) PairingDetailForSki(ski string) *api.ConnectionStateDetail { - service := h.ServiceForSKI(ski) - - if conn := h.connectionForSKI(ski); conn != nil { - shipState, shipError := conn.ShipHandshakeState() - state := h.mapShipMessageExchangeState(shipState, ski) - return api.NewConnectionStateDetail(state, shipError) - } - - return service.ConnectionStateDetail() -} - -// maps ShipMessageExchangeState to PairingState -func (h *connectionsHubImpl) mapShipMessageExchangeState(state shipmodel.ShipMessageExchangeState, ski string) api.ConnectionState { - var connState api.ConnectionState - - // map the SHIP states to a public gState - switch state { - case shipmodel.CmiStateInitStart: - connState = api.ConnectionStateQueued - case shipmodel.CmiStateClientSend, shipmodel.CmiStateClientWait, shipmodel.CmiStateClientEvaluate, - shipmodel.CmiStateServerWait, shipmodel.CmiStateServerEvaluate: - connState = api.ConnectionStateInitiated - case shipmodel.SmeHelloStateReadyInit, shipmodel.SmeHelloStateReadyListen, shipmodel.SmeHelloStateReadyTimeout: - connState = api.ConnectionStateInProgress - case shipmodel.SmeHelloStatePendingInit, shipmodel.SmeHelloStatePendingListen, shipmodel.SmeHelloStatePendingTimeout: - connState = api.ConnectionStateReceivedPairingRequest - case shipmodel.SmeHelloStateOk: - connState = api.ConnectionStateTrusted - case shipmodel.SmeHelloStateAbort, shipmodel.SmeHelloStateAbortDone: - connState = api.ConnectionStateNone - case shipmodel.SmeHelloStateRemoteAbortDone, shipmodel.SmeHelloStateRejected: - connState = api.ConnectionStateRemoteDeniedTrust - case shipmodel.SmePinStateCheckInit, shipmodel.SmePinStateCheckListen, shipmodel.SmePinStateCheckError, - shipmodel.SmePinStateCheckBusyInit, shipmodel.SmePinStateCheckBusyWait, shipmodel.SmePinStateCheckOk, - shipmodel.SmePinStateAskInit, shipmodel.SmePinStateAskProcess, shipmodel.SmePinStateAskRestricted, - shipmodel.SmePinStateAskOk: - connState = api.ConnectionStatePin - case shipmodel.SmeAccessMethodsRequest, shipmodel.SmeStateApproved: - connState = api.ConnectionStateInProgress - case shipmodel.SmeStateComplete: - connState = api.ConnectionStateCompleted - case shipmodel.SmeStateError: - connState = api.ConnectionStateError - default: - connState = api.ConnectionStateInProgress - } - - return connState -} - -// Disconnect a connection to an SKI, used by a service implementation -// e.g. if heartbeats go wrong -func (h *connectionsHubImpl) DisconnectSKI(ski string, reason string) { - h.muxCon.Lock() - defer h.muxCon.Unlock() - - // The connection with the higher SKI should retain the connection - con, ok := h.connections[ski] - if !ok { - return - } - - con.CloseConnection(true, 0, reason) -} - -// register a new ship Connection -func (h *connectionsHubImpl) registerConnection(connection shipapi.ShipConnection) { - h.muxCon.Lock() - h.connections[connection.RemoteSKI()] = connection - h.muxCon.Unlock() -} - -// return the connection for a specific SKI -func (h *connectionsHubImpl) connectionForSKI(ski string) shipapi.ShipConnection { - h.muxCon.Lock() - defer h.muxCon.Unlock() - - con, ok := h.connections[ski] - if !ok { - return nil - } - return con -} - -// close all connections -func (h *connectionsHubImpl) Shutdown() { - h.mdns.ShutdownMdnsService() - for _, c := range h.connections { - c.CloseConnection(false, 0, "") - } -} - -// return if there is a connection for a SKI -func (h *connectionsHubImpl) isSkiConnected(ski string) bool { - h.muxCon.Lock() - defer h.muxCon.Unlock() - - // The connection with the higher SKI should retain the connection - _, ok := h.connections[ski] - return ok -} - -// Websocket connection handling -func (h *connectionsHubImpl) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - skiFound := false - for _, v := range rawCerts { - cerificate, err := x509.ParseCertificate(v) - if err != nil { - return err - } - - if _, err := cert.SkiFromCertificate(cerificate); err == nil { - skiFound = true - break - } - } - if !skiFound { - return errors.New("no valid SKI provided in certificate") - } - - return nil -} - -// start the ship websocket server -func (h *connectionsHubImpl) startWebsocketServer() error { - addr := fmt.Sprintf(":%d", h.configuration.Port()) - logging.Log().Debug("starting websocket server on", addr) - - h.httpServer = &http.Server{ - Addr: addr, - Handler: h, - TLSConfig: &tls.Config{ - Certificates: []tls.Certificate{h.configuration.Certificate()}, - ClientAuth: tls.RequireAnyClientCert, // SHIP 9: Client authentication is required - CipherSuites: cert.CiperSuites, - VerifyPeerCertificate: h.verifyPeerCertificate, - }, - } - - go func() { - if err := h.httpServer.ListenAndServeTLS("", ""); err != nil { - logging.Log().Debug("websocket server error:", err) - // TODO: decide how to handle this case - } - }() - - return nil -} - -// Connection Handling - -// HTTP Server callback for handling incoming connection requests -func (h *connectionsHubImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { - upgrader := websocket.Upgrader{ - ReadBufferSize: shipws.MaxMessageSize, - WriteBufferSize: shipws.MaxMessageSize, - CheckOrigin: func(r *http.Request) bool { return true }, - Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, // SHIP 10.2: Sub protocol "ship" is required - } - - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - logging.Log().Debug("error during connection upgrading:", err) - return - } - - // check if the client supports the ship sub protocol - if conn.Subprotocol() != shipapi.ShipWebsocketSubProtocol { - logging.Log().Debug("client does not support the ship sub protocol") - _ = conn.Close() - return - } - - // check if the clients certificate provides a SKI - if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 { - logging.Log().Debug("client does not provide a certificate") - _ = conn.Close() - return - } - - ski, err := cert.SkiFromCertificate(r.TLS.PeerCertificates[0]) - if err != nil { - logging.Log().Debug(err) - _ = conn.Close() - return - } - - // normalize the incoming SKI - remoteService := api.NewServiceDetails(ski) - logging.Log().Debug("incoming connection request from", remoteService.SKI) - - // Check if the remote service is paired - service := h.ServiceForSKI(remoteService.SKI) - connectionStateDetail := service.ConnectionStateDetail() - if connectionStateDetail.State() == api.ConnectionStateQueued { - connectionStateDetail.SetState(api.ConnectionStateReceivedPairingRequest) - h.serviceProvider.ServicePairingDetailUpdate(ski, connectionStateDetail) - } - - remoteService = service - - // don't allow a second connection - if !h.keepThisConnection(conn, true, remoteService) { - _ = conn.Close() - return - } - - dataHandler := shipws.NewWebsocketConnection(conn, remoteService.SKI) - shipConnection := ship.NewConnectionHandler(h, dataHandler, ship.ShipRoleServer, - h.localService.ShipID, remoteService.SKI, remoteService.ShipID) - shipConnection.Run() - - h.registerConnection(shipConnection) -} - -// Connect to another EEBUS service -// -// returns error contains a reason for failing the connection or nil if no further tries should be processed -func (h *connectionsHubImpl) connectFoundService(remoteService *api.ServiceDetails, host, port string) error { - if h.isSkiConnected(remoteService.SKI) { - return nil - } - - logging.Log().Debugf("initiating connection to %s at %s:%s", remoteService.SKI, host, port) - - dialer := &websocket.Dialer{ - Proxy: http.ProxyFromEnvironment, - HandshakeTimeout: 5 * time.Second, - TLSClientConfig: &tls.Config{ - Certificates: []tls.Certificate{h.configuration.Certificate()}, - InsecureSkipVerify: true, - CipherSuites: cert.CiperSuites, - }, - Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, - } - - address := fmt.Sprintf("wss://%s:%s", host, port) - conn, _, err := dialer.Dial(address, nil) - if err != nil { - return err - } - - tlsConn := conn.UnderlyingConn().(*tls.Conn) - remoteCerts := tlsConn.ConnectionState().PeerCertificates - - if len(remoteCerts) == 0 || remoteCerts[0].SubjectKeyId == nil { - // Close connection as we couldn't get the remote SKI - errorString := fmt.Sprintf("closing connection to %s: could not get remote SKI from certificate", remoteService.SKI) - conn.Close() - return errors.New(errorString) - } - - if _, err := cert.SkiFromCertificate(remoteCerts[0]); err != nil { - // Close connection as the remote SKI can't be correct - errorString := fmt.Sprintf("closing connection to %s: %s", remoteService.SKI, err) - conn.Close() - return errors.New(errorString) - } - - remoteSKI := fmt.Sprintf("%0x", remoteCerts[0].SubjectKeyId) - - if remoteSKI != remoteService.SKI { - errorString := fmt.Sprintf("closing connection to %s: SKI does not match %s", remoteService.SKI, remoteSKI) - conn.Close() - return errors.New(errorString) - } - - if !h.keepThisConnection(conn, false, remoteService) { - errorString := fmt.Sprintf("closing connection to %s: ignoring this connection", remoteService.SKI) - return errors.New(errorString) - } - - dataHandler := shipws.NewWebsocketConnection(conn, remoteService.SKI) - shipConnection := ship.NewConnectionHandler(h, dataHandler, ship.ShipRoleClient, - h.localService.ShipID, remoteService.SKI, remoteService.ShipID) - shipConnection.Run() - - h.registerConnection(shipConnection) - - return nil -} - -// prevent double connections -// only keep the connection initiated by the higher SKI -// -// returns true if this connection is fine to be continue -// returns false if this connection should not be established or kept -func (h *connectionsHubImpl) keepThisConnection(conn *websocket.Conn, incomingRequest bool, remoteService *api.ServiceDetails) bool { - // SHIP 12.2.2 defines: - // prevent double connections with SKI Comparison - // the node with the hight SKI value kees the most recent connection and - // and closes all other connections to the same SHIP node - // - // This is hard to implement without any flaws. Therefor I chose a - // different approach: The connection initiated by the higher SKI will be kept - - remoteSKI := remoteService.SKI - existingC := h.connectionForSKI(remoteSKI) - if existingC == nil { - return true - } - - keep := false - if incomingRequest { - keep = remoteSKI > h.localService.SKI - } else { - keep = h.localService.SKI > remoteSKI - } - - if keep { - // we have an existing connection - // so keep the new (most recent) and close the old one - logging.Log().Debug("closing existing double connection") - go existingC.CloseConnection(false, 0, "") - } else { - connType := "incoming" - if !incomingRequest { - connType = "outgoing" - } - logging.Log().Debugf("closing %s double connection, as the existing connection will be used", connType) - if conn != nil { - go func() { - _ = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "double connection")) - time.Sleep(time.Millisecond * 100) - conn.Close() - }() - } - } - - return keep -} - -// return the service for a given SKI or an error if not found -func (h *connectionsHubImpl) ServiceForSKI(ski string) *api.ServiceDetails { - h.muxReg.Lock() - defer h.muxReg.Unlock() - - service, ok := h.remoteServices[ski] - if !ok { - service = api.NewServiceDetails(ski) - service.ConnectionStateDetail().SetState(api.ConnectionStateNone) - h.remoteServices[ski] = service - } - - return service -} - -// Sets the SKI as being paired or not -// Should be used for services which completed the pairing process and -// which were stored as having the process completed -func (h *connectionsHubImpl) RegisterRemoteSKI(ski string, enable bool) { - service := h.ServiceForSKI(ski) - service.Trusted = enable - - if enable { - h.checkRestartMdnsSearch() - return - } - - h.removeConnectionAttemptCounter(ski) - - service.ConnectionStateDetail().SetState(api.ConnectionStateNone) - - h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) - - if existingC := h.connectionForSKI(ski); existingC != nil { - existingC.CloseConnection(true, 4500, "User close") - } -} - -// Triggers the pairing process for a SKI -func (h *connectionsHubImpl) InitiatePairingWithSKI(ski string) { - conn := h.connectionForSKI(ski) - - // remotely initiated - if conn != nil { - conn.ApprovePendingHandshake() - - return - } - - // locally initiated - service := h.ServiceForSKI(ski) - service.ConnectionStateDetail().SetState(api.ConnectionStateQueued) - - h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) - - // initiate a search and also a connection if it does not yet exist - if !h.isSkiConnected(service.SKI) { - h.mdns.RegisterMdnsSearch(h) - } -} - -// Cancels the pairing process for a SKI -func (h *connectionsHubImpl) CancelPairingWithSKI(ski string) { - h.removeConnectionAttemptCounter(ski) - - if existingC := h.connectionForSKI(ski); existingC != nil { - existingC.AbortPendingHandshake() - } - - service := h.ServiceForSKI(ski) - service.ConnectionStateDetail().SetState(api.ConnectionStateNone) - service.Trusted = false - - h.serviceProvider.ServicePairingDetailUpdate(ski, service.ConnectionStateDetail()) -} - -// Process reported mDNS services -func (h *connectionsHubImpl) ReportMdnsEntries(entries map[string]*shipapi.MdnsEntry) { - h.muxMdns.Lock() - defer h.muxMdns.Unlock() - - var mdnsEntries []*shipapi.MdnsEntry - - for ski, entry := range entries { - mdnsEntries = append(mdnsEntries, entry) - - // check if this ski is already connected - if h.isSkiConnected(ski) { - continue - } - - // Check if the remote service is paired or queued for connection - service := h.ServiceForSKI(ski) - if !h.IsRemoteServiceForSKIPaired(ski) && - service.ConnectionStateDetail().State() != api.ConnectionStateQueued { - continue - } - - // patch the addresses list if an IPv4 address was provided - if service.IPv4 != "" { - if ip := net.ParseIP(service.IPv4); ip != nil { - entry.Addresses = []net.IP{ip} - } - } - - h.coordinateConnectionInitations(ski, entry) - } - - sort.Slice(mdnsEntries, func(i, j int) bool { - item1 := mdnsEntries[i] - item2 := mdnsEntries[j] - a := strings.ToLower(item1.Brand + item1.Model + item1.Ski) - b := strings.ToLower(item2.Brand + item2.Model + item2.Ski) - return a < b - }) - - h.knownMdnsEntries = mdnsEntries - - h.serviceProvider.VisibleMDNSRecordsUpdated(mdnsEntries) -} - -// coordinate connection initiation attempts to a remove service -func (h *connectionsHubImpl) coordinateConnectionInitations(ski string, entry *shipapi.MdnsEntry) { - if h.isConnectionAttemptRunning(ski) { - return - } - - h.setConnectionAttemptRunning(ski, true) - - counter, duration := h.getConnectionInitiationDelayTime(ski) - - service := h.ServiceForSKI(ski) - if service.ConnectionStateDetail().State() == api.ConnectionStateQueued { - go h.prepareConnectionInitation(ski, counter, entry) - return - } - - logging.Log().Debugf("delaying connection to %s by %s to minimize double connection probability", ski, duration) - - // we do not stop this thread and just let the timer run out - // otherwise we would need a stop channel for each ski - go func() { - // wait - <-time.After(duration) - - h.prepareConnectionInitation(ski, counter, entry) - }() -} - -// invoked by coordinateConnectionInitations either with a delay or directly -// when initating a pairing process -func (h *connectionsHubImpl) prepareConnectionInitation(ski string, counter int, entry *shipapi.MdnsEntry) { - h.setConnectionAttemptRunning(ski, false) - - // check if the current counter is still the same, otherwise this counter is irrelevant - currentCounter, exists := h.getCurrentConnectionAttemptCounter(ski) - if !exists || currentCounter != counter { - return - } - - // connection attempt is not relevant if the device is no longer paired - // or it is not queued for pairing - pairingState := h.ServiceForSKI(ski).ConnectionStateDetail().State() - if !h.IsRemoteServiceForSKIPaired(ski) && pairingState != api.ConnectionStateQueued { - return - } - - // connection attempt is not relevant if the device is already connected - if h.isSkiConnected(ski) { - return - } - - // now initiate the connection - // check if the remoteService still exists - service := h.ServiceForSKI(ski) - - if success := h.initateConnection(service, entry); !success { - h.checkRestartMdnsSearch() - } -} - -// attempt to establish a connection to a remote service -// returns true if successful -func (h *connectionsHubImpl) initateConnection(remoteService *api.ServiceDetails, entry *shipapi.MdnsEntry) bool { - var err error - - // try connecting via an IP address first - for _, address := range entry.Addresses { - // connection attempt is not relevant if the device is no longer paired - // or it is not queued for pairing - pairingState := h.ServiceForSKI(remoteService.SKI).ConnectionStateDetail().State() - if !h.IsRemoteServiceForSKIPaired(remoteService.SKI) && pairingState != api.ConnectionStateQueued { - return false - } - - logging.Log().Debug("trying to connect to", remoteService.SKI, "at", address) - if err = h.connectFoundService(remoteService, address.String(), strconv.Itoa(entry.Port)); err != nil { - logging.Log().Debug("connection to", remoteService.SKI, "failed: ", err) - } else { - return true - } - } - - // connectdion via IP address failed, try hostname - if len(entry.Host) > 0 { - logging.Log().Debug("trying to connect to", remoteService.SKI, "at", entry.Host) - if err = h.connectFoundService(remoteService, entry.Host, strconv.Itoa(entry.Port)); err != nil { - logging.Log().Debugf("connection to %s failed: %s", remoteService.SKI, err) - } else { - return true - } - } - - // no connection could be estabished via any of the provided addresses - // because no service was reachable at any of the addresses - return false -} - -// increase the connection attempt counter for the given ski -func (h *connectionsHubImpl) increaseConnectionAttemptCounter(ski string) int { - h.muxConAttempt.Lock() - defer h.muxConAttempt.Unlock() - - currentCounter := 0 - if counter, exists := h.connectionAttemptCounter[ski]; exists { - currentCounter = counter + 1 - - if currentCounter >= len(connectionInitiationDelayTimeRanges)-1 { - currentCounter = len(connectionInitiationDelayTimeRanges) - 1 - } - } - - h.connectionAttemptCounter[ski] = currentCounter - - return currentCounter -} - -// remove the connection attempt counter for the given ski -func (h *connectionsHubImpl) removeConnectionAttemptCounter(ski string) { - h.muxConAttempt.Lock() - defer h.muxConAttempt.Unlock() - - delete(h.connectionAttemptCounter, ski) -} - -// get the current attempt counter -func (h *connectionsHubImpl) getCurrentConnectionAttemptCounter(ski string) (int, bool) { - h.muxConAttempt.Lock() - defer h.muxConAttempt.Unlock() - - counter, exists := h.connectionAttemptCounter[ski] - - return counter, exists -} - -// get the connection initiation delay time range for a given ski -// returns the current counter and the duration -func (h *connectionsHubImpl) getConnectionInitiationDelayTime(ski string) (int, time.Duration) { - counter := h.increaseConnectionAttemptCounter(ski) - - h.muxConAttempt.Lock() - defer h.muxConAttempt.Unlock() - - timeRange := connectionInitiationDelayTimeRanges[counter] - - // get range in Milliseconds - min := timeRange.min * 1000 - max := timeRange.max * 1000 - - // seed with the local SKI for initializing rand - // TODO: remove when upping minimum go version to 1.20 - i := new(big.Int) - hex := fmt.Sprintf("0x%s", h.localService.SKI) - randSource := rand.NewSource(time.Now().UnixNano()) - if _, err := fmt.Sscan(hex, i); err == nil { - randSource = rand.NewSource(i.Int64() + time.Now().UnixNano()) - } - - r := rand.New(randSource) - duration := r.Intn(max-min) + min - - return counter, time.Duration(duration) * time.Millisecond -} - -// set if a connection attempt is running/in progress -func (h *connectionsHubImpl) setConnectionAttemptRunning(ski string, active bool) { - h.muxConAttempt.Lock() - defer h.muxConAttempt.Unlock() - - h.connectionAttemptRunning[ski] = active -} - -// return if a connection attempt is runnning/in progress -func (h *connectionsHubImpl) isConnectionAttemptRunning(ski string) bool { - h.muxConAttempt.Lock() - defer h.muxConAttempt.Unlock() - - running, exists := h.connectionAttemptRunning[ski] - if !exists { - return false - } - - return running -} diff --git a/service/hub_test.go b/service/hub_test.go deleted file mode 100644 index 961f7929..00000000 --- a/service/hub_test.go +++ /dev/null @@ -1,641 +0,0 @@ -package service - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "errors" - "math/big" - "net" - "net/http" - "net/http/httptest" - "net/url" - "strings" - "testing" - "time" - - "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/mocks" - shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/cert" - shipmocks "github.com/enbility/ship-go/mocks" - shipmodel "github.com/enbility/ship-go/model" - "github.com/enbility/spine-go/model" - "github.com/gorilla/websocket" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" - "go.uber.org/mock/gomock" -) - -func TestHubSuite(t *testing.T) { - suite.Run(t, new(HubSuite)) -} - -type testStruct struct { - counter int - timeRange connectionInitiationDelayTimeRange -} - -type HubSuite struct { - suite.Suite - - serviceProvider *mocks.MockServiceProvider - mdnsService *shipmocks.MockMdnsService - - // serviceProvider *mocks.ServiceProvider - // mdnsService *mocks.MdnsService - shipConnection *shipmocks.ShipConnection - wsDataConnection *shipmocks.WebsocketDataConnection - - remoteSki string - - tests []testStruct - - sut *connectionsHubImpl -} - -func (s *HubSuite) BeforeTest(suiteName, testName string) { - s.remoteSki = "remotetestski" - - s.tests = []testStruct{ - {0, connectionInitiationDelayTimeRanges[0]}, - {1, connectionInitiationDelayTimeRanges[1]}, - {2, connectionInitiationDelayTimeRanges[2]}, - {3, connectionInitiationDelayTimeRanges[2]}, - {4, connectionInitiationDelayTimeRanges[2]}, - {5, connectionInitiationDelayTimeRanges[2]}, - {6, connectionInitiationDelayTimeRanges[2]}, - {7, connectionInitiationDelayTimeRanges[2]}, - {8, connectionInitiationDelayTimeRanges[2]}, - {9, connectionInitiationDelayTimeRanges[2]}, - {10, connectionInitiationDelayTimeRanges[2]}, - } - - ctrl := gomock.NewController(s.T()) - // use gomock mocks instead of mockery, as those will panic with a data race error in these tests - - s.serviceProvider = mocks.NewMockServiceProvider(ctrl) - // s.serviceProvider = mocks.NewServiceProvider(s.T()) - s.serviceProvider.EXPECT().RemoteSKIConnected(gomock.Any()).Return().AnyTimes() - s.serviceProvider.EXPECT().ServiceShipIDUpdate(gomock.Any(), gomock.Any()).Return().AnyTimes() - s.serviceProvider.EXPECT().ServicePairingDetailUpdate(gomock.Any(), gomock.Any()).Return().AnyTimes() - s.serviceProvider.EXPECT().RemoteSKIDisconnected(gomock.Any()).Return().AnyTimes() - s.serviceProvider.EXPECT().AllowWaitingForTrust(gomock.Any()).Return(false).AnyTimes() - - s.mdnsService = shipmocks.NewMockMdnsService(ctrl) - // s.mdnsService = mocks.NewMdnsService(s.T()) - s.mdnsService.EXPECT().SetupMdnsService().Return(nil).AnyTimes() - s.mdnsService.EXPECT().AnnounceMdnsEntry().Return(nil).AnyTimes() - s.mdnsService.EXPECT().UnannounceMdnsEntry().Return().AnyTimes() - s.mdnsService.EXPECT().RegisterMdnsSearch(gomock.Any()).Return().AnyTimes() - s.mdnsService.EXPECT().UnregisterMdnsSearch(gomock.Any()).Return().AnyTimes() - - s.wsDataConnection = shipmocks.NewWebsocketDataConnection(s.T()) - - s.shipConnection = shipmocks.NewShipConnection(s.T()) - s.shipConnection.EXPECT().CloseConnection(mock.Anything, mock.Anything, mock.Anything).Return().Maybe() - s.shipConnection.EXPECT().RemoteSKI().Return(s.remoteSki).Maybe() - s.shipConnection.EXPECT().ApprovePendingHandshake().Return().Maybe() - s.shipConnection.EXPECT().AbortPendingHandshake().Return().Maybe() - s.shipConnection.EXPECT().DataHandler().Return(s.wsDataConnection).Maybe() - s.shipConnection.EXPECT().ShipHandshakeState().Return(shipmodel.SmeStateComplete, nil).Maybe() - - localService := &api.ServiceDetails{ - SKI: "localSKI", - } - - s.sut = &connectionsHubImpl{ - connections: make(map[string]shipapi.ShipConnection), - connectionAttemptCounter: make(map[string]int), - connectionAttemptRunning: make(map[string]bool), - remoteServices: make(map[string]*api.ServiceDetails), - serviceProvider: s.serviceProvider, - localService: localService, - mdns: s.mdnsService, - } - - certificate, _ := cert.CreateCertificate("unit", "org", "DE", "CN") - s.sut.configuration, _ = api.NewConfiguration("vendor", "brand", "model", "serial", - model.DeviceTypeTypeGeneric, []model.EntityTypeType{model.EntityTypeTypeCEM}, - 4567, certificate, 230, time.Second*4) -} - -func (s *HubSuite) Test_NewConnectionsHub() { - ski := "12af9e" - localService := api.NewServiceDetails(ski) - - configuration := &api.Configuration{} - configuration.SetInterfaces([]string{"en0"}) - - hub := newConnectionsHub(s.serviceProvider, s.mdnsService, nil, configuration, localService) - assert.NotNil(s.T(), hub) - - hub.Start() -} - -func (s *HubSuite) Test_IsRemoteSKIPaired() { - paired := s.sut.IsRemoteServiceForSKIPaired(s.remoteSki) - assert.Equal(s.T(), false, paired) - - s.sut.registerConnection(s.shipConnection) - s.sut.RegisterRemoteSKI(s.remoteSki, true) - - paired = s.sut.IsRemoteServiceForSKIPaired(s.remoteSki) - assert.Equal(s.T(), true, paired) - - // remove the connection, so the test doesn't try to close it - delete(s.sut.connections, s.remoteSki) - s.sut.RegisterRemoteSKI(s.remoteSki, false) - paired = s.sut.IsRemoteServiceForSKIPaired(s.remoteSki) - assert.Equal(s.T(), false, paired) -} - -func (s *HubSuite) Test_HandleConnecitonClosed() { - s.sut.HandleConnectionClosed(s.shipConnection, false) - - s.sut.registerConnection(s.shipConnection) - - s.sut.HandleConnectionClosed(s.shipConnection, true) - - assert.Equal(s.T(), 0, len(s.sut.connections)) -} - -func (s *HubSuite) Test_Mdns() { - s.sut.checkRestartMdnsSearch() - - pairedServices := s.sut.numberPairedServices() - assert.Equal(s.T(), 0, len(s.sut.connections)) - assert.Equal(s.T(), 0, pairedServices) - - s.sut.RegisterRemoteSKI(s.remoteSki, true) - pairedServices = s.sut.numberPairedServices() - assert.Equal(s.T(), 0, len(s.sut.connections)) - assert.Equal(s.T(), 1, pairedServices) - - s.sut.StartBrowseMdnsSearch() - - s.sut.StopBrowseMdnsSearch() -} - -func (s *HubSuite) Test_Ship() { - s.sut.HandleShipHandshakeStateUpdate(s.remoteSki, shipmodel.ShipState{ - State: shipmodel.SmeStateError, - Error: errors.New("test"), - }) - - s.sut.HandleShipHandshakeStateUpdate(s.remoteSki, shipmodel.ShipState{ - State: shipmodel.SmeHelloStateOk, - }) - - s.sut.ReportServiceShipID(s.remoteSki, "test") - - trust := s.sut.AllowWaitingForTrust(s.remoteSki) - assert.Equal(s.T(), true, trust) - - trust = s.sut.AllowWaitingForTrust("test") - assert.Equal(s.T(), false, trust) - - detail := s.sut.PairingDetailForSki(s.remoteSki) - assert.NotNil(s.T(), detail) - - s.sut.registerConnection(s.shipConnection) - - detail = s.sut.PairingDetailForSki(s.remoteSki) - assert.NotNil(s.T(), detail) -} - -func (s *HubSuite) Test_MapShipMessageExchangeState() { - state := s.sut.mapShipMessageExchangeState(shipmodel.CmiStateInitStart, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateQueued, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.CmiStateClientSend, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateInitiated, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStateReadyInit, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateInProgress, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStatePendingInit, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateReceivedPairingRequest, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStateOk, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateTrusted, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStateAbort, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateNone, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeHelloStateRemoteAbortDone, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateRemoteDeniedTrust, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmePinStateCheckInit, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStatePin, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeAccessMethodsRequest, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateInProgress, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeStateComplete, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateCompleted, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeStateError, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateError, state) - - state = s.sut.mapShipMessageExchangeState(shipmodel.SmeProtHStateTimeout, s.remoteSki) - assert.Equal(s.T(), api.ConnectionStateInProgress, state) -} - -func (s *HubSuite) Test_DisconnectSKI() { - s.sut.DisconnectSKI(s.remoteSki, "none") -} - -func (s *HubSuite) Test_RegisterConnection() { - s.sut.registerConnection(s.shipConnection) - assert.Equal(s.T(), 1, len(s.sut.connections)) - con := s.sut.connectionForSKI(s.remoteSki) - assert.NotNil(s.T(), con) -} - -func (s *HubSuite) Test_Shutdown() { - s.mdnsService.EXPECT().ShutdownMdnsService() - s.sut.Shutdown() -} - -func (s *HubSuite) Test_VerifyPeerCertificate() { - testCert, _ := cert.CreateCertificate("unit", "org", "DE", "CN") - var rawCerts [][]byte - rawCerts = append(rawCerts, testCert.Certificate...) - err := s.sut.verifyPeerCertificate(rawCerts, nil) - assert.Nil(s.T(), err) - - rawCerts = nil - rawCerts = append(rawCerts, []byte{100}) - err = s.sut.verifyPeerCertificate(rawCerts, nil) - assert.NotNil(s.T(), err) - - rawCerts = nil - invalidCert, _ := createInvalidCertificate("unit", "org", "DE", "CN") - rawCerts = append(rawCerts, invalidCert.Certificate...) - - err = s.sut.verifyPeerCertificate(rawCerts, nil) - assert.NotNil(s.T(), err) -} - -func (s *HubSuite) Test_ServeHTTP_01() { - req := httptest.NewRequest("GET", "http://example.com/foo", nil) - w := httptest.NewRecorder() - s.sut.ServeHTTP(w, req) - - server := httptest.NewServer(s.sut) - wsURL := strings.Replace(server.URL, "http://", "ws://", -1) - - // Connect to the server - con, _, err := websocket.DefaultDialer.Dial(wsURL, nil) - assert.Nil(s.T(), err) - con.Close() - - dialer := &websocket.Dialer{ - Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, - } - con, _, err = dialer.Dial(wsURL, nil) - assert.Nil(s.T(), err) - - con.Close() - server.CloseClientConnections() - server.Close() - - time.Sleep(time.Second) -} - -func (s *HubSuite) Test_ServeHTTP_02() { - server := httptest.NewUnstartedServer(s.sut) - server.TLS = &tls.Config{ - Certificates: []tls.Certificate{s.sut.configuration.Certificate()}, - ClientAuth: tls.RequireAnyClientCert, - CipherSuites: cert.CiperSuites, - InsecureSkipVerify: true, - } - server.StartTLS() - wsURL := strings.Replace(server.URL, "https://", "wss://", -1) - - invalidCert, _ := createInvalidCertificate("unit", "org", "DE", "CN") - dialer := &websocket.Dialer{ - Proxy: http.ProxyFromEnvironment, - HandshakeTimeout: 5 * time.Second, - TLSClientConfig: &tls.Config{ - Certificates: []tls.Certificate{invalidCert}, - InsecureSkipVerify: true, - CipherSuites: cert.CiperSuites, - }, - Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, - } - con, _, err := dialer.Dial(wsURL, nil) - assert.Nil(s.T(), err) - - con.Close() - - validCert, _ := cert.CreateCertificate("unit", "org", "DE", "CN") - dialer = &websocket.Dialer{ - Proxy: http.ProxyFromEnvironment, - HandshakeTimeout: 5 * time.Second, - TLSClientConfig: &tls.Config{ - Certificates: []tls.Certificate{validCert}, - InsecureSkipVerify: true, - CipherSuites: cert.CiperSuites, - }, - Subprotocols: []string{shipapi.ShipWebsocketSubProtocol}, - } - con, _, err = dialer.Dial(wsURL, nil) - assert.Nil(s.T(), err) - - con.Close() - server.CloseClientConnections() - server.Close() - - time.Sleep(time.Second) -} - -func (s *HubSuite) Test_ConnectFoundService_01() { - service := s.sut.ServiceForSKI(s.remoteSki) - - err := s.sut.connectFoundService(service, "localhost", "80") - assert.NotNil(s.T(), err) - - server := httptest.NewServer(s.sut) - url, err := url.Parse(server.URL) - assert.Nil(s.T(), err) - - err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) - assert.NotNil(s.T(), err) - - server.CloseClientConnections() - server.Close() - - time.Sleep(time.Second) -} - -func (s *HubSuite) Test_ConnectFoundService_02() { - service := s.sut.ServiceForSKI(s.remoteSki) - - server := httptest.NewUnstartedServer(s.sut) - invalidCert, _ := createInvalidCertificate("unit", "org", "DE", "CN") - server.TLS = &tls.Config{ - Certificates: []tls.Certificate{invalidCert}, - ClientAuth: tls.RequireAnyClientCert, - CipherSuites: cert.CiperSuites, - InsecureSkipVerify: true, - } - server.StartTLS() - - url, err := url.Parse(server.URL) - assert.Nil(s.T(), err) - - err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) - assert.NotNil(s.T(), err) - - server.CloseClientConnections() - server.Close() - - time.Sleep(time.Second) -} - -func (s *HubSuite) Test_ConnectFoundService_03() { - service := s.sut.ServiceForSKI(s.remoteSki) - - server := httptest.NewUnstartedServer(s.sut) - server.TLS = &tls.Config{ - Certificates: []tls.Certificate{s.sut.configuration.Certificate()}, - ClientAuth: tls.RequireAnyClientCert, - CipherSuites: cert.CiperSuites, - InsecureSkipVerify: true, - } - server.StartTLS() - - url, err := url.Parse(server.URL) - assert.Nil(s.T(), err) - - err = s.sut.connectFoundService(service, url.Hostname(), url.Port()) - assert.NotNil(s.T(), err) - - time.Sleep(time.Second) - - server.CloseClientConnections() - server.Close() -} - -func (s *HubSuite) Test_KeepThisConnection() { - service := s.sut.ServiceForSKI(s.remoteSki) - - result := s.sut.keepThisConnection(nil, false, service) - assert.Equal(s.T(), true, result) - - s.sut.registerConnection(s.shipConnection) - - result = s.sut.keepThisConnection(nil, false, service) - assert.Equal(s.T(), false, result) - - result = s.sut.keepThisConnection(nil, true, service) - assert.Equal(s.T(), true, result) -} - -func (s *HubSuite) Test_prepareConnectionInitiation() { - entry := &shipapi.MdnsEntry{ - Ski: s.remoteSki, - Host: "somehost", - } - service := s.sut.ServiceForSKI(s.remoteSki) - - s.sut.prepareConnectionInitation(s.remoteSki, 0, entry) - - s.sut.setConnectionAttemptRunning(s.remoteSki, true) - - counter := s.sut.increaseConnectionAttemptCounter(s.remoteSki) - assert.Equal(s.T(), 0, counter) - s.sut.prepareConnectionInitation(s.remoteSki, 0, entry) - - s.sut.RegisterRemoteSKI(s.remoteSki, false) - service.ConnectionStateDetail().SetState(api.ConnectionStateQueued) - - counter = s.sut.increaseConnectionAttemptCounter(s.remoteSki) - assert.Equal(s.T(), 0, counter) - - s.sut.prepareConnectionInitation(s.remoteSki, 0, entry) -} - -func (s *HubSuite) Test_InitiateConnection() { - entry := &shipapi.MdnsEntry{ - Ski: s.remoteSki, - Host: "somehost", - } - service := s.sut.ServiceForSKI(s.remoteSki) - - result := s.sut.initateConnection(service, entry) - assert.Equal(s.T(), false, result) - - entry.Addresses = []net.IP{[]byte("127.0.0.1")} - - result = s.sut.initateConnection(service, entry) - assert.Equal(s.T(), false, result) - - s.sut.RegisterRemoteSKI(s.remoteSki, true) - service.ConnectionStateDetail().SetState(api.ConnectionStateQueued) - - result = s.sut.initateConnection(service, entry) - assert.Equal(s.T(), false, result) -} - -func (s *HubSuite) Test_IncreaseConnectionAttemptCounter() { - for _, test := range s.tests { - s.sut.increaseConnectionAttemptCounter(s.remoteSki) - - s.sut.muxConAttempt.Lock() - counter, exists := s.sut.connectionAttemptCounter[s.remoteSki] - timeRange := connectionInitiationDelayTimeRanges[counter] - s.sut.muxConAttempt.Unlock() - - assert.Equal(s.T(), true, exists) - assert.Equal(s.T(), test.timeRange.min, timeRange.min) - assert.Equal(s.T(), test.timeRange.max, timeRange.max) - } -} - -func (s *HubSuite) Test_RemoveConnectionAttemptCounter() { - s.sut.increaseConnectionAttemptCounter(s.remoteSki) - _, exists := s.sut.connectionAttemptCounter[s.remoteSki] - assert.Equal(s.T(), true, exists) - - s.sut.removeConnectionAttemptCounter(s.remoteSki) - _, exists = s.sut.connectionAttemptCounter[s.remoteSki] - assert.Equal(s.T(), false, exists) -} - -func (s *HubSuite) Test_GetCurrentConnectionAttemptCounter() { - s.sut.increaseConnectionAttemptCounter(s.remoteSki) - _, exists := s.sut.connectionAttemptCounter[s.remoteSki] - assert.Equal(s.T(), exists, true) - s.sut.increaseConnectionAttemptCounter(s.remoteSki) - - value, exists := s.sut.getCurrentConnectionAttemptCounter(s.remoteSki) - assert.Equal(s.T(), 1, value) - assert.Equal(s.T(), true, exists) -} - -func (s *HubSuite) Test_GetConnectionInitiationDelayTime() { - counter, duration := s.sut.getConnectionInitiationDelayTime(s.remoteSki) - assert.Equal(s.T(), 0, counter) - assert.LessOrEqual(s.T(), float64(s.tests[counter].timeRange.min), float64(duration/time.Second)) - assert.GreaterOrEqual(s.T(), float64(s.tests[counter].timeRange.max), float64(duration/time.Second)) -} - -func (s *HubSuite) Test_ConnectionAttemptRunning() { - s.sut.setConnectionAttemptRunning(s.remoteSki, true) - status := s.sut.isConnectionAttemptRunning(s.remoteSki) - assert.Equal(s.T(), true, status) - s.sut.setConnectionAttemptRunning(s.remoteSki, false) - status = s.sut.isConnectionAttemptRunning(s.remoteSki) - assert.Equal(s.T(), false, status) -} - -func (s *HubSuite) Test_InitiatePairingWithSKI() { - s.sut.InitiatePairingWithSKI(s.remoteSki) - assert.Equal(s.T(), 0, len(s.sut.connections)) - - s.sut.registerConnection(s.shipConnection) - s.sut.InitiatePairingWithSKI(s.remoteSki) - assert.Equal(s.T(), 1, len(s.sut.connections)) -} - -func (s *HubSuite) Test_CancelPairingWithSKI() { - s.sut.CancelPairingWithSKI(s.remoteSki) - assert.Equal(s.T(), 0, len(s.sut.connections)) - assert.Equal(s.T(), 0, len(s.sut.connectionAttemptRunning)) - - s.sut.registerConnection(s.shipConnection) - assert.Equal(s.T(), 1, len(s.sut.connections)) - - s.sut.CancelPairingWithSKI(s.remoteSki) - assert.Equal(s.T(), 0, len(s.sut.connectionAttemptRunning)) -} - -func (s *HubSuite) Test_ReportMdnsEntries() { - testski1 := "test1" - testski2 := "test2" - - entries := make(map[string]*shipapi.MdnsEntry) - - s.serviceProvider.EXPECT().VisibleMDNSRecordsUpdated(gomock.Any()).AnyTimes() - s.sut.ReportMdnsEntries(entries) - - entries[testski1] = &shipapi.MdnsEntry{ - Ski: testski1, - } - service1 := s.sut.ServiceForSKI(testski1) - service1.Trusted = true - service1.IPv4 = "127.0.0.1" - - entries[testski2] = &shipapi.MdnsEntry{ - Ski: testski2, - } - service2 := s.sut.ServiceForSKI(testski2) - service2.Trusted = true - service2.IPv4 = "127.0.0.1" - - s.sut.ReportMdnsEntries(entries) -} - -func createInvalidCertificate(organizationalUnit, organization, country, commonName string) (tls.Certificate, error) { - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return tls.Certificate{}, err - } - - // Create the EEBUS service SKI using the private key - asn1, err := x509.MarshalECPrivateKey(privateKey) - if err != nil { - return tls.Certificate{}, err - } - // SHIP 12.2: Required to be created according to RFC 3280 4.2.1.2 - ski := sha1.Sum(asn1) - - subject := pkix.Name{ - OrganizationalUnit: []string{organizationalUnit}, - Organization: []string{organization}, - Country: []string{country}, - CommonName: commonName, - } - - // Create a random serial big int value - maxValue := new(big.Int) - maxValue.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxValue, big.NewInt(1)) - serialNumber, err := rand.Int(rand.Reader, maxValue) - if err != nil { - return tls.Certificate{}, err - } - - template := x509.Certificate{ - SignatureAlgorithm: x509.ECDSAWithSHA256, - SerialNumber: serialNumber, - Subject: subject, - NotBefore: time.Now(), // Valid starting now - NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10), // Valid for 10 years - KeyUsage: x509.KeyUsageDigitalSignature, - BasicConstraintsValid: true, - IsCA: true, - SubjectKeyId: ski[:19], - } - - certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) - if err != nil { - return tls.Certificate{}, err - } - - tlsCertificate := tls.Certificate{ - Certificate: [][]byte{certBytes}, - PrivateKey: privateKey, - SupportedSignatureAlgorithms: []tls.SignatureScheme{tls.ECDSAWithP256AndSHA256}, - } - - return tlsCertificate, nil -} diff --git a/service/service.go b/service/service.go index df3b8ad5..481d1112 100644 --- a/service/service.go +++ b/service/service.go @@ -9,6 +9,7 @@ import ( "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/cert" + "github.com/enbility/ship-go/hub" "github.com/enbility/ship-go/logging" "github.com/enbility/ship-go/mdns" spineapi "github.com/enbility/spine-go/api" @@ -22,10 +23,10 @@ type EEBUSServiceImpl struct { configuration *api.Configuration // The local service details - localService *api.ServiceDetails + localService *shipapi.ServiceDetails // Connection Registrations - connectionsHub api.ConnectionsHub + connectionsHub shipapi.Hub // The SPINE specific device definition spineLocalDevice spineapi.DeviceLocal @@ -43,13 +44,13 @@ func NewEEBUSService(configuration *api.Configuration, serviceHandler api.EEBUSS } } -var _ api.ServiceProvider = (*EEBUSServiceImpl)(nil) +var _ shipapi.HubConnection = (*EEBUSServiceImpl)(nil) func (s *EEBUSServiceImpl) VisibleMDNSRecordsUpdated(entries []*shipapi.MdnsEntry) { - var remoteServices []api.RemoteService + var remoteServices []shipapi.RemoteService for _, entry := range entries { - remoteService := api.RemoteService{ + remoteService := shipapi.RemoteService{ Name: entry.Name, Ski: entry.Ski, Identifier: entry.Identifier, @@ -70,6 +71,10 @@ func (s *EEBUSServiceImpl) RemoteSKIConnected(ski string) { // report a disconnection to a SKI func (s *EEBUSServiceImpl) RemoteSKIDisconnected(ski string) { + if s.spineLocalDevice != nil { + s.spineLocalDevice.RemoveRemoteDeviceConnection(ski) + } + s.serviceHandler.RemoteSKIDisconnected(s, ski) } @@ -81,10 +86,14 @@ func (s *EEBUSServiceImpl) ServiceShipIDUpdate(ski string, shipdID string) { // Provides the current pairing state for the remote service // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process -func (s *EEBUSServiceImpl) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) { +func (s *EEBUSServiceImpl) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { s.serviceHandler.ServicePairingDetailUpdate(ski, detail) } +func (s *EEBUSServiceImpl) SetupRemoteDevice(ski string, writeI shipapi.SpineDataConnection) shipapi.SpineDataProcessing { + return s.LocalDevice().SetupRemoteDevice(ski, writeI) +} + // return if the user is still able to trust the connection func (s *EEBUSServiceImpl) AllowWaitingForTrust(ski string) bool { return s.serviceHandler.AllowWaitingForTrust(ski) @@ -93,7 +102,7 @@ func (s *EEBUSServiceImpl) AllowWaitingForTrust(ski string) bool { var _ api.EEBUSService = (*EEBUSServiceImpl)(nil) // Get the current pairing details for a given SKI -func (s *EEBUSServiceImpl) PairingDetailForSki(ski string) *api.ConnectionStateDetail { +func (s *EEBUSServiceImpl) PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail { return s.connectionsHub.PairingDetailForSki(ski) } @@ -142,9 +151,9 @@ func (s *EEBUSServiceImpl) Setup() error { // The originator's unique ID // I assume those two to mean the same. // TODO: clarify - s.localService = api.NewServiceDetails(ski) + s.localService = shipapi.NewServiceDetails(ski) s.localService.ShipID = sd.Identifier() - s.localService.DeviceType = sd.DeviceType() + s.localService.DeviceType = string(sd.DeviceType()) s.localService.RegisterAutoAccept = sd.RegisterAutoAccept() logging.Log().Info("Local SKI: ", ski) @@ -188,7 +197,7 @@ func (s *EEBUSServiceImpl) Setup() error { sd.Identifier(), sd.MdnsServiceName(), sd.Port(), sd.Interfaces()) // Setup connections hub with mDNS and websocket connection handling - s.connectionsHub = newConnectionsHub(s, mdns, s.spineLocalDevice, s.configuration, s.localService) + s.connectionsHub = hub.NewHub(s, mdns, s.configuration.Port(), s.configuration.Certificate(), s.localService) return nil } @@ -210,7 +219,7 @@ func (s *EEBUSServiceImpl) Configuration() *api.Configuration { return s.configuration } -func (s *EEBUSServiceImpl) LocalService() *api.ServiceDetails { +func (s *EEBUSServiceImpl) LocalService() *shipapi.ServiceDetails { return s.localService } @@ -219,7 +228,7 @@ func (s *EEBUSServiceImpl) LocalDevice() spineapi.DeviceLocal { } // Returns the Service detail of a given remote SKI -func (s *EEBUSServiceImpl) RemoteServiceForSKI(ski string) *api.ServiceDetails { +func (s *EEBUSServiceImpl) RemoteServiceForSKI(ski string) *shipapi.ServiceDetails { return s.connectionsHub.ServiceForSKI(ski) } diff --git a/service/service_test.go b/service/service_test.go index f692c77a..7d430a99 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -29,14 +29,14 @@ type ServiceSuite struct { sut *EEBUSServiceImpl serviceHandler *mocks.EEBUSServiceHandler - conHub *mocks.ConnectionsHub + conHub *shipmocks.Hub logging *shipmocks.Logging } func (s *ServiceSuite) BeforeTest(suiteName, testName string) { s.serviceHandler = mocks.NewEEBUSServiceHandler(s.T()) - s.conHub = mocks.NewConnectionsHub(s.T()) + s.conHub = shipmocks.NewHub(s.T()) s.logging = shipmocks.NewLogging(s.T()) @@ -69,7 +69,7 @@ func (s *ServiceSuite) Test_EEBUSHandler() { s.sut.ServiceShipIDUpdate(testSki, "shipid") s.serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return() - detail := &api.ConnectionStateDetail{} + detail := &shipapi.ConnectionStateDetail{} s.sut.ServicePairingDetailUpdate(testSki, detail) s.serviceHandler.EXPECT().AllowWaitingForTrust(mock.Anything).Return(true) diff --git a/util/channel.go b/util/channel.go deleted file mode 100644 index 98a226c8..00000000 --- a/util/channel.go +++ /dev/null @@ -1,11 +0,0 @@ -package util - -// check if a provided channel is closed -func IsChannelClosed[T any](ch <-chan T) bool { - select { - case <-ch: - return false - default: - return true - } -} diff --git a/util/helper.go b/util/helper.go deleted file mode 100644 index ec095f12..00000000 --- a/util/helper.go +++ /dev/null @@ -1,27 +0,0 @@ -package util - -import ( - "encoding/json" - "os" - "strings" -) - -// standardize the provided SKI strings -func NormalizeSKI(ski string) string { - ski = strings.ReplaceAll(ski, " ", "") - ski = strings.ReplaceAll(ski, "-", "") - ski = strings.ToLower(ski) - - return ski -} - -// quick way to a struct into another -func DeepCopy[A any](source, dest A) { - byt, _ := json.Marshal(source) - _ = json.Unmarshal(byt, dest) -} - -// used in tests -func IsRunningOnCI() bool { - return os.Getenv("ACTION_ENVIRONMENT") == "CI" -} From 4b43b3731e46c0920dcb07c74081c43aa0e8f533 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 19 Jan 2024 18:25:58 +0100 Subject: [PATCH 161/240] Fix possible panics --- features/feature.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/features/feature.go b/features/feature.go index 55add157..cb8520ed 100644 --- a/features/feature.go +++ b/features/feature.go @@ -44,6 +44,9 @@ func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole mod // subscribe to the feature for a the entity func (f *FeatureImpl) SubscribeForEntity() error { + if f.featureRemote == nil { + return errors.New("remote feature not available") + } if _, fErr := f.featureLocal.Subscribe(f.featureRemote.Address()); fErr != nil { return errors.New(fErr.String()) } @@ -57,6 +60,9 @@ func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType // bind to the feature of a the entity func (f *FeatureImpl) Bind() error { + if f.featureRemote == nil { + return errors.New("remote feature not available") + } if _, fErr := f.featureLocal.Bind(f.featureRemote.Address()); fErr != nil { return errors.New(fErr.String()) } From 1e1d0f8cd5ef4fcb2f3c6c0b0d9ae74f59e5b4cb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 19 Jan 2024 18:27:24 +0100 Subject: [PATCH 162/240] Update ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8081d056..343170ae 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.18 require ( - github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4 - github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c + github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 + github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index a9b63445..c8ad28fe 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4 h1:udB9WMUnI/YvoW8armfvylz0iP4ISjWdWbgI7yRO+Jk= -github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= -github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= +github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 h1:CphcdnbDvpzU+/uyy0k9v08Z4ngmYLVWCFbghOhircY= +github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= +github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710 h1:WL550WMZIv3fUrFjJ5qSMD17On9Kb1n17JW/eBx4/Ak= +github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From b29ee60146a7dcc78a87b736421d0a8f75b2592c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 20 Jan 2024 15:20:50 +0100 Subject: [PATCH 163/240] Update to latest spine --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 343170ae..7826a15c 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 - github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710 + github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index c8ad28fe..41a5dd76 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 h1:CphcdnbDvpzU+/uyy0k9v08Z4ngmYLVWCFbghOhircY= github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710 h1:WL550WMZIv3fUrFjJ5qSMD17On9Kb1n17JW/eBx4/Ak= -github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= +github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca h1:k2lWJM60QxOitR83mHL85n3fxL6Rvzfmp3BZQ55BULU= +github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca/go.mod h1:Ce7CrTfyW01BPudqELY99L9Fyo9d1mn1ogUqpB0vRX0= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From c246dc7222d03807f8a5ea45f30be84cd40fabab Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 11:39:52 +0100 Subject: [PATCH 164/240] Update github action --- .github/workflows/default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 7cad577e..d14843a7 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ^1.18 From 521794de4bff7afc3486d5bc58e91ab1871a2157 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 11:40:02 +0100 Subject: [PATCH 165/240] Update ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 7826a15c..9bd1917d 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.18 require ( - github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 - github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca + github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29 + github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 41a5dd76..7e071f4c 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 h1:CphcdnbDvpzU+/uyy0k9v08Z4ngmYLVWCFbghOhircY= -github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca h1:k2lWJM60QxOitR83mHL85n3fxL6Rvzfmp3BZQ55BULU= -github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca/go.mod h1:Ce7CrTfyW01BPudqELY99L9Fyo9d1mn1ogUqpB0vRX0= +github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29 h1:zAyFAxT6sB8m/AoD95215v4Vr5Rp1vbrbYI7pdrAJnc= +github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= +github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f h1:6S5FahaQeRW/qBPAkVgKkvo1HWOeSLOPPUHqqpBMiEs= +github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f/go.mod h1:tOPz6dr3m+s+h1Ouu78EQS5kB9Buh3Pnu2jW6eQrwOM= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From 3c0fef02f1c5e3e54c395c67c8e920edd0434fd4 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 14:15:15 +0100 Subject: [PATCH 166/240] Update ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9bd1917d..4d9509fb 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.18 require ( - github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29 - github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f + github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6 + github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 7e071f4c..447e7540 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29 h1:zAyFAxT6sB8m/AoD95215v4Vr5Rp1vbrbYI7pdrAJnc= -github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f h1:6S5FahaQeRW/qBPAkVgKkvo1HWOeSLOPPUHqqpBMiEs= -github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f/go.mod h1:tOPz6dr3m+s+h1Ouu78EQS5kB9Buh3Pnu2jW6eQrwOM= +github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6 h1:Xgzy+Jv5HxwJtnDRk1IVStxYvGd/hWFzKU8Z6qgH8FA= +github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= +github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd h1:DyHn0ql/JBu2olfUWiLmCGbXMpNwMMivrEtxxHrF+Sw= +github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd/go.mod h1:n6ITUaTl0XASZWRafB7hSdIVLKO7gxlZ+95CN+uHktQ= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From 27c0b876f02dfa5d12610705cb0dbb17d3af35e3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 14:19:04 +0100 Subject: [PATCH 167/240] Fix codefactor warnings and add it to readme --- README.md | 1 + api/configuration.go | 24 ++++++++++++------------ features/timeseries.go | 3 +++ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 71392bc7..e7dd69fb 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4)](https://godoc.org/github.com/enbility/eebus-go) [![Coverage Status](https://coveralls.io/repos/github/enbility/eebus-go/badge.svg?branch=dev)](https://coveralls.io/github/enbility/eebus-go?branch=dev) [![Go report](https://goreportcard.com/badge/github.com/enbility/eebus-go)](https://goreportcard.com/report/github.com/enbility/eebus-go) +[![CodeFactor](https://www.codefactor.io/repository/github/enbility/eebus-go/badge)](https://www.codefactor.io/repository/github/enbility/eebus-go) This library provides a foundation for implementing [EEBUS](https://eebus.org) use cases in [go](https://golang.org). It uses the SHIP implementation [ship-go](https://github.com/enbility/ship-go) and the SPINE implementation [spine-go](https://github.com/enbility/spine-go). Both repositories started as part of this repository, before they were moved into their own separate repositories and go packages. diff --git a/api/configuration.go b/api/configuration.go index fa65feee..250a3129 100644 --- a/api/configuration.go +++ b/api/configuration.go @@ -109,34 +109,34 @@ func NewConfiguration( if len(vendorCode) == 0 { return nil, fmt.Errorf("vendorCode %s", isRequired) - } else { - configuration.vendorCode = vendorCode } + configuration.vendorCode = vendorCode + if len(deviceBrand) == 0 { return nil, fmt.Errorf("brand %s", isRequired) - } else { - configuration.deviceBrand = deviceBrand } + configuration.deviceBrand = deviceBrand + if len(deviceModel) == 0 { return nil, fmt.Errorf("model %s", isRequired) - } else { - configuration.deviceModel = deviceModel } + configuration.deviceModel = deviceModel + if len(serialNumber) == 0 { return nil, fmt.Errorf("serialNumber %s", isRequired) - } else { - configuration.deviceSerialNumber = serialNumber } + configuration.deviceSerialNumber = serialNumber + if len(deviceType) == 0 { return nil, fmt.Errorf("deviceType %s", isRequired) - } else { - configuration.deviceType = deviceType } + configuration.deviceType = deviceType + if len(entityTypes) == 0 { return nil, fmt.Errorf("entityTypes %s", isRequired) - } else { - configuration.entityTypes = entityTypes } + configuration.entityTypes = entityTypes + // set default configuration.featureSet = model.NetworkManagementFeatureSetTypeSmart diff --git a/features/timeseries.go b/features/timeseries.go index 230cba35..1a0f5cc1 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -146,6 +146,9 @@ func (t *TimeSeries) GetDescriptionForType(timeSeriesType model.TimeSeriesTypeTy // return current constraints for Time Series func (t *TimeSeries) GetConstraints() ([]model.TimeSeriesConstraintsDataType, error) { rData := t.featureRemote.DataCopy(model.FunctionTypeTimeSeriesConstraintsListData) + + // the codefactor warning is invalid, as .(type) check can not be replaced with if then + //revive:disable-next-line switch constraintsData := rData.(type) { case *model.TimeSeriesConstraintsListDataType: if constraintsData == nil { From df8d436f2edf65938ea4b1bb2d0bf121d6fa353a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 15:54:39 +0100 Subject: [PATCH 168/240] Update to go 1.21.1 and update ship, spine --- go.mod | 6 +++--- go.sum | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 4d9509fb..400ee0b9 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/enbility/eebus-go -go 1.18 +go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6 - github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd + github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6 + github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 447e7540..1cc4b8d7 100644 --- a/go.sum +++ b/go.sum @@ -5,13 +5,14 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6 h1:Xgzy+Jv5HxwJtnDRk1IVStxYvGd/hWFzKU8Z6qgH8FA= -github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd h1:DyHn0ql/JBu2olfUWiLmCGbXMpNwMMivrEtxxHrF+Sw= -github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd/go.mod h1:n6ITUaTl0XASZWRafB7hSdIVLKO7gxlZ+95CN+uHktQ= +github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6 h1:dWZ5qjUPjZhtUviZmsTkuE+6xKSJrp34yx7Xb3iShQ0= +github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366 h1:AEszB6babmfq9Bvx/iU7D0lolOTWERgs8hPyytV0IA0= +github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366/go.mod h1:VrxQBPf7SaaLRPNRe9OOY0e954oF/pFtO3glqSulpGg= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -22,6 +23,7 @@ github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SW github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rickb777/date v1.20.5 h1:Ybjz7J7ga9ui4VJizQpil0l330r6wkn6CicaoattIxQ= @@ -66,6 +68,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -90,6 +93,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From 8f78a6caf619e5a9bce7f762792dfaf2edcdb1fc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 15:54:54 +0100 Subject: [PATCH 169/240] Add gosec to github action --- .github/workflows/default.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index d14843a7..a4e514ca 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -42,4 +42,15 @@ jobs: - name: Send coverage uses: shogo82148/actions-goveralls@v1 with: - path-to-profile: coverage.out \ No newline at end of file + path-to-profile: coverage.out + + - name: Run Gosec Security Scanner + uses: securego/gosec@master + with: + # we let the report trigger content trigger a failure using the GitHub Security features. + args: '-no-fail -fmt sarif -out results.sarif ./...' + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v2 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: results.sarif \ No newline at end of file From f617499b3ee9784b96e930a4b3137c99da53d52a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 15:55:05 +0100 Subject: [PATCH 170/240] Ignore gosec warning in test --- integration_tests/helper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/helper_test.go b/integration_tests/helper_test.go index 2ae7de20..64c30d01 100644 --- a/integration_tests/helper_test.go +++ b/integration_tests/helper_test.go @@ -106,7 +106,7 @@ func initialCommunication(t *testing.T, remoteDevice spineapi.DeviceRemote, writ } func loadFileData(t *testing.T, fileName string) []byte { - fileData, err := os.ReadFile(fileName) + fileData, err := os.ReadFile(fileName) // #nosec G304 if err != nil { t.Fatal(err) } From b9cc1396dd65ed334d420087f2b1eeafcdc752dc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 16:19:25 +0100 Subject: [PATCH 171/240] Uupdate ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 400ee0b9..cec1bb68 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6 - github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366 + github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f + github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 1cc4b8d7..e31744c1 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6 h1:dWZ5qjUPjZhtUviZmsTkuE+6xKSJrp34yx7Xb3iShQ0= -github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366 h1:AEszB6babmfq9Bvx/iU7D0lolOTWERgs8hPyytV0IA0= -github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366/go.mod h1:VrxQBPf7SaaLRPNRe9OOY0e954oF/pFtO3glqSulpGg= +github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f h1:6yWUydMGGS+PD2KiA+vNOVThpgK6MzGIU7bVjPQ646M= +github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43 h1:mbuwnwfh9v3UeoSNkczDuBicwbZUAQvbqGI8YevlWww= +github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43/go.mod h1:BcNplM7cqwn27J7v06IJs8RZy5aSRRz0t2QTX4lcsdI= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From e3405300a6656ec99df91f3d499fce8690c3ae4d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 17:36:06 +0100 Subject: [PATCH 172/240] Update to latest ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- service/service.go | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index cec1bb68..98232309 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f - github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43 + github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a + github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index e31744c1..68345298 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f h1:6yWUydMGGS+PD2KiA+vNOVThpgK6MzGIU7bVjPQ646M= -github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43 h1:mbuwnwfh9v3UeoSNkczDuBicwbZUAQvbqGI8YevlWww= -github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43/go.mod h1:BcNplM7cqwn27J7v06IJs8RZy5aSRRz0t2QTX4lcsdI= +github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a h1:BWK59DNijiUQ4lQYxAKjSLd/7t4sSmt0ptfapwcDAUw= +github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd h1:N/ii735+ElnoHGqwUJ1BnDG4YZXIVxuHXw5vVsBArV8= +github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd/go.mod h1:7VQRShAU9yVnFhl9sUwFV0Yvy8gZsHMZzeLUs60AVVI= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= diff --git a/service/service.go b/service/service.go index 481d1112..49c47cef 100644 --- a/service/service.go +++ b/service/service.go @@ -152,9 +152,9 @@ func (s *EEBUSServiceImpl) Setup() error { // I assume those two to mean the same. // TODO: clarify s.localService = shipapi.NewServiceDetails(ski) - s.localService.ShipID = sd.Identifier() - s.localService.DeviceType = string(sd.DeviceType()) - s.localService.RegisterAutoAccept = sd.RegisterAutoAccept() + s.localService.SetShipID(sd.Identifier()) + s.localService.SetDeviceType(string(sd.DeviceType())) + s.localService.SetRegisterAutoAccept(sd.RegisterAutoAccept()) logging.Log().Info("Local SKI: ", ski) @@ -193,7 +193,7 @@ func (s *EEBUSServiceImpl) Setup() error { // setup mDNS mdns := mdns.NewMDNS( - s.localService.SKI, sd.DeviceBrand(), sd.DeviceModel(), string(sd.DeviceType()), + s.localService.SKI(), sd.DeviceBrand(), sd.DeviceModel(), string(sd.DeviceType()), sd.Identifier(), sd.MdnsServiceName(), sd.Port(), sd.Interfaces()) // Setup connections hub with mDNS and websocket connection handling From ae6f0b3fb3cf15d98a4a758ec4b76558bfaed14c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 18:48:58 +0100 Subject: [PATCH 173/240] Update SHIP and SPINE - Support for too early SPINE messages - Add error logging if handling SPINE messages failed --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 98232309..cae09d79 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a - github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd + github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec + github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 68345298..ae77fdc3 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a h1:BWK59DNijiUQ4lQYxAKjSLd/7t4sSmt0ptfapwcDAUw= -github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd h1:N/ii735+ElnoHGqwUJ1BnDG4YZXIVxuHXw5vVsBArV8= -github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd/go.mod h1:7VQRShAU9yVnFhl9sUwFV0Yvy8gZsHMZzeLUs60AVVI= +github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec h1:Bu0hu9rsJIkUCtg64YJZte5OaGx0LStrmS10ufEma6o= +github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41 h1:FoeVeEesGzKoFL1YRD+DS65Jm5O2vXqXl5/5k2WSbxg= +github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41/go.mod h1:v/MAO6HzXJ8fzvWkC2454W+bHmPO8Ry7lvRj/eWmREQ= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From 38836994d74c1b4d939d29aa481e8758c3541102 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 23 Jan 2024 20:12:39 +0100 Subject: [PATCH 174/240] Refactor namings --- api/api.go | 54 +- cmd/evse/main.go | 10 +- cmd/hems/main.go | 10 +- features/deviceclassification.go | 5 +- features/deviceclassification_test.go | 8 +- features/deviceconfiguration.go | 5 +- features/deviceconfiguration_test.go | 8 +- features/devicediagnosis.go | 5 +- features/devicediagnosis_test.go | 8 +- features/electricalconnection.go | 5 +- features/electricalconnection_test.go | 8 +- features/feature.go | 16 +- features/helper_test.go | 17 +- features/identification.go | 5 +- features/identification_test.go | 8 +- features/incentivetable.go | 5 +- features/incentivetable_test.go | 8 +- features/loadcontrol.go | 5 +- features/loadcontrol_test.go | 8 +- features/measurement.go | 5 +- features/measurement_test.go | 8 +- features/timeseries.go | 5 +- features/timeseries_test.go | 8 +- go.mod | 4 +- go.sum | 8 +- .../emobility_measurement_test.go | 14 +- integration_tests/helper_test.go | 6 +- mocks/EEBUSService.go | 618 ------------------ mocks/EEBUSServiceHandler.go | 253 ------- mocks/ServiceInterface.go | 618 ++++++++++++++++++ mocks/ServiceReaderInterface.go | 253 +++++++ service/service.go | 64 +- service/service_test.go | 28 +- 33 files changed, 1060 insertions(+), 1030 deletions(-) delete mode 100644 mocks/EEBUSService.go delete mode 100644 mocks/EEBUSServiceHandler.go create mode 100644 mocks/ServiceInterface.go create mode 100644 mocks/ServiceReaderInterface.go diff --git a/api/api.go b/api/api.go index 71caff98..0c7c253e 100644 --- a/api/api.go +++ b/api/api.go @@ -9,18 +9,39 @@ import ( //go:generate mockery -/* EEBUSService */ +/* Service */ -// interface for receiving data for specific events from EEBUSService -type EEBUSServiceHandler interface { +type ServiceInterface interface { + Setup() error + Start() + Shutdown() + SetLogging(logger logging.LoggingInterface) + + Configuration() *Configuration + LocalService() *shipapi.ServiceDetails + LocalDevice() spineapi.DeviceLocalInterface + RemoteServiceForSKI(ski string) *shipapi.ServiceDetails + RegisterRemoteSKI(ski string, enable bool) + InitiatePairingWithSKI(ski string) + CancelPairingWithSKI(ski string) + DisconnectSKI(ski string, reason string) + + // Passthough functions to ConnectionsHub + PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail + StartBrowseMdnsEntries() + StopBrowseMdnsEntries() +} + +// interface for receiving data for specific events from Service +type ServiceReaderInterface interface { // report all currently visible EEBUS services - VisibleRemoteServicesUpdated(service EEBUSService, entries []shipapi.RemoteService) + VisibleRemoteServicesUpdated(service ServiceInterface, entries []shipapi.RemoteService) // report a connection to a SKI - RemoteSKIConnected(service EEBUSService, ski string) + RemoteSKIConnected(service ServiceInterface, ski string) // report a disconnection to a SKI - RemoteSKIDisconnected(service EEBUSService, ski string) + RemoteSKIDisconnected(service ServiceInterface, ski string) // Provides the SHIP ID the remote service reported during the handshake process // This needs to be persisted and passed on for future remote service connections @@ -35,24 +56,3 @@ type EEBUSServiceHandler interface { // return if the user is still able to trust the connection AllowWaitingForTrust(ski string) bool } - -type EEBUSService interface { - Setup() error - Start() - Shutdown() - SetLogging(logger logging.Logging) - - Configuration() *Configuration - LocalService() *shipapi.ServiceDetails - LocalDevice() spineapi.DeviceLocal - RemoteServiceForSKI(ski string) *shipapi.ServiceDetails - RegisterRemoteSKI(ski string, enable bool) - InitiatePairingWithSKI(ski string) - CancelPairingWithSKI(ski string) - DisconnectSKI(ski string, reason string) - - // Passthough functions to ConnectionsHub - PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail - StartBrowseMdnsEntries() - StopBrowseMdnsEntries() -} diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 44d583e5..09771752 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -23,7 +23,7 @@ import ( var remoteSki string type evse struct { - myService *service.EEBUSServiceImpl + myService *service.Service } func (h *evse) run() { @@ -74,7 +74,7 @@ func (h *evse) run() { } configuration.SetAlternateIdentifier("Demo-EVSE-234567890") - h.myService = service.NewEEBUSService(configuration, h) + h.myService = service.NewService(configuration, h) h.myService.SetLogging(h) if err = h.myService.Setup(); err != nil { @@ -94,11 +94,11 @@ func (h *evse) run() { // EEBUSServiceHandler -func (h *evse) RemoteSKIConnected(service api.EEBUSService, ski string) {} +func (h *evse) RemoteSKIConnected(service api.ServiceInterface, ski string) {} -func (h *evse) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} +func (h *evse) RemoteSKIDisconnected(service api.ServiceInterface, ski string) {} -func (h *evse) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []shipapi.RemoteService) { +func (h *evse) VisibleRemoteServicesUpdated(service api.ServiceInterface, entries []shipapi.RemoteService) { } func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} diff --git a/cmd/hems/main.go b/cmd/hems/main.go index 0961f993..f89c38fb 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -23,7 +23,7 @@ import ( var remoteSki string type hems struct { - myService *service.EEBUSServiceImpl + myService *service.Service } func (h *hems) run() { @@ -74,7 +74,7 @@ func (h *hems) run() { } configuration.SetAlternateIdentifier("Demo-HEMS-123456789") - h.myService = service.NewEEBUSService(configuration, h) + h.myService = service.NewService(configuration, h) h.myService.SetLogging(h) if err = h.myService.Setup(); err != nil { @@ -94,11 +94,11 @@ func (h *hems) run() { // EEBUSServiceHandler -func (h *hems) RemoteSKIConnected(service api.EEBUSService, ski string) {} +func (h *hems) RemoteSKIConnected(service api.ServiceInterface, ski string) {} -func (h *hems) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} +func (h *hems) RemoteSKIDisconnected(service api.ServiceInterface, ski string) {} -func (h *hems) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []shipapi.RemoteService) { +func (h *hems) VisibleRemoteServicesUpdated(service api.ServiceInterface, entries []shipapi.RemoteService) { } func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} diff --git a/features/deviceclassification.go b/features/deviceclassification.go index 49b3f82b..17c6cdfd 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -9,7 +9,10 @@ type DeviceClassification struct { *FeatureImpl } -func NewDeviceClassification(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*DeviceClassification, error) { +func NewDeviceClassification( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*DeviceClassification, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/deviceclassification_test.go b/features/deviceclassification_test.go index 7423b328..f9180161 100644 --- a/features/deviceclassification_test.go +++ b/features/deviceclassification_test.go @@ -19,16 +19,16 @@ func TestDeviceClassificationSuite(t *testing.T) { type DeviceClassificationSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface deviceClassification *features.DeviceClassification sentMessage []byte } -var _ shipapi.SpineDataConnection = (*DeviceClassificationSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*DeviceClassificationSuite)(nil) -func (s *DeviceClassificationSuite) WriteSpineMessage(message []byte) { +func (s *DeviceClassificationSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index 6db80aa4..717d842a 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -9,7 +9,10 @@ type DeviceConfiguration struct { *FeatureImpl } -func NewDeviceConfiguration(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*DeviceConfiguration, error) { +func NewDeviceConfiguration( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*DeviceConfiguration, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index 9c90bccf..4534e99d 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -19,16 +19,16 @@ func TestDeviceConfigurationSuite(t *testing.T) { type DeviceConfigurationSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface deviceConfiguration *features.DeviceConfiguration sentMessage []byte } -var _ shipapi.SpineDataConnection = (*DeviceConfigurationSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*DeviceConfigurationSuite)(nil) -func (s *DeviceConfigurationSuite) WriteSpineMessage(message []byte) { +func (s *DeviceConfigurationSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index d3af75ea..37a43666 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -9,7 +9,10 @@ type DeviceDiagnosis struct { *FeatureImpl } -func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*DeviceDiagnosis, error) { +func NewDeviceDiagnosis( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*DeviceDiagnosis, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 1dbcb064..16f9fc2f 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -19,16 +19,16 @@ func TestDeviceDiagnosisSuite(t *testing.T) { type DeviceDiagnosisSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface deviceDiagnosis *features.DeviceDiagnosis sentMessage []byte } -var _ shipapi.SpineDataConnection = (*DeviceDiagnosisSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*DeviceDiagnosisSuite)(nil) -func (s *DeviceDiagnosisSuite) WriteSpineMessage(message []byte) { +func (s *DeviceDiagnosisSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/features/electricalconnection.go b/features/electricalconnection.go index 680fc435..b8fb63a8 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -9,7 +9,10 @@ type ElectricalConnection struct { *FeatureImpl } -func NewElectricalConnection(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*ElectricalConnection, error) { +func NewElectricalConnection( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*ElectricalConnection, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 11a54453..e58c0617 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -19,16 +19,16 @@ func TestElectricalConnectionSuite(t *testing.T) { type ElectricalConnectionSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface electricalConnection *features.ElectricalConnection sentMessage []byte } -var _ shipapi.SpineDataConnection = (*ElectricalConnectionSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*ElectricalConnectionSuite)(nil) -func (s *ElectricalConnectionSuite) WriteSpineMessage(message []byte) { +func (s *ElectricalConnectionSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/features/feature.go b/features/feature.go index cb8520ed..b31658a9 100644 --- a/features/feature.go +++ b/features/feature.go @@ -13,19 +13,19 @@ type FeatureImpl struct { localRole model.RoleType remoteRole model.RoleType - spineLocalDevice api.DeviceLocal - localEntity api.EntityLocal + spineLocalDevice api.DeviceLocalInterface + localEntity api.EntityLocalInterface - featureLocal api.FeatureLocal - featureRemote api.FeatureRemote + featureLocal api.FeatureLocalInterface + featureRemote api.FeatureRemoteInterface - remoteDevice api.DeviceRemote - remoteEntity api.EntityRemote + remoteDevice api.DeviceRemoteInterface + remoteEntity api.EntityRemoteInterface } var _ Feature = (*FeatureImpl)(nil) -func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*FeatureImpl, error) { +func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*FeatureImpl, error) { f := &FeatureImpl{ featureType: featureType, localRole: localRole, @@ -97,7 +97,7 @@ func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, el } // internal helper method for getting local and remote feature for a given featureType and a given remoteDevice -func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (api.FeatureLocal, api.FeatureRemote, error) { +func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (api.FeatureLocalInterface, api.FeatureRemoteInterface, error) { if f.remoteEntity == nil { return nil, nil, errors.New("invalid remote entity provided") } diff --git a/features/helper_test.go b/features/helper_test.go index e3273009..d21549d4 100644 --- a/features/helper_test.go +++ b/features/helper_test.go @@ -24,9 +24,9 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ shipapi.SpineDataConnection = (*WriteMessageHandler)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*WriteMessageHandler)(nil) -func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { +func (t *WriteMessageHandler) WriteShipMessageWithPayload(message []byte) { t.mux.Lock() defer t.mux.Unlock() @@ -96,20 +96,23 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg return nil } -func setupFeatures(t assert.TestingT, dataCon shipapi.SpineDataConnection, featureFunctions []featureFunctions) (spineapi.EntityLocal, spineapi.EntityRemote) { - localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", +func setupFeatures( + t assert.TestingT, + dataCon shipapi.ShipConnectionDataWriterInterface, + featureFunctions []featureFunctions) (spineapi.EntityLocalInterface, spineapi.EntityRemoteInterface) { + localDevice := spine.NewDeviceLocal("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + localEntity := spine.NewEntityLocal(localDevice, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) localDevice.AddEntity(localEntity) for i, item := range featureFunctions { - f := spine.NewFeatureLocalImpl(uint(i+1), localEntity, item.featureType, model.RoleTypeServer) + f := spine.NewFeatureLocal(uint(i+1), localEntity, item.featureType, model.RoleTypeServer) localEntity.AddFeature(f) } remoteDeviceName := "remoteDevice" sender := spine.NewSender(dataCon) - remoteDevice := spine.NewDeviceRemoteImpl(localDevice, "test", sender) + remoteDevice := spine.NewDeviceRemote(localDevice, "test", sender) data := &model.NodeManagementDetailedDiscoveryDataType{ DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ Description: &model.NetworkManagementDeviceDescriptionDataType{ diff --git a/features/identification.go b/features/identification.go index 226efc64..c065c002 100644 --- a/features/identification.go +++ b/features/identification.go @@ -9,7 +9,10 @@ type Identification struct { *FeatureImpl } -func NewIdentification(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*Identification, error) { +func NewIdentification( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*Identification, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeIdentification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/identification_test.go b/features/identification_test.go index 71048530..85b236d2 100644 --- a/features/identification_test.go +++ b/features/identification_test.go @@ -19,16 +19,16 @@ func TestIdentificationSuite(t *testing.T) { type IdentificationSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface identification *features.Identification sentMessage []byte } -var _ shipapi.SpineDataConnection = (*IdentificationSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*IdentificationSuite)(nil) -func (s *IdentificationSuite) WriteSpineMessage(message []byte) { +func (s *IdentificationSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/features/incentivetable.go b/features/incentivetable.go index f1b4a633..3f21b5ef 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -9,7 +9,10 @@ type IncentiveTable struct { *FeatureImpl } -func NewIncentiveTable(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*IncentiveTable, error) { +func NewIncentiveTable( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*IncentiveTable, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 43a4a26b..41ab9315 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -19,16 +19,16 @@ func TestIncentiveTableSuite(t *testing.T) { type IncentiveTableSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface incentiveTable *features.IncentiveTable sentMessage []byte } -var _ shipapi.SpineDataConnection = (*IncentiveTableSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*IncentiveTableSuite)(nil) -func (s *IncentiveTableSuite) WriteSpineMessage(message []byte) { +func (s *IncentiveTableSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 62442c03..06201495 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -9,7 +9,10 @@ type LoadControl struct { *FeatureImpl } -func NewLoadControl(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*LoadControl, error) { +func NewLoadControl( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*LoadControl, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeLoadControl, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index 7acde9ac..28e29245 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -19,16 +19,16 @@ func TestLoadControlSuite(t *testing.T) { type LoadControlSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface loadControl *features.LoadControl sentMessage []byte } -var _ shipapi.SpineDataConnection = (*LoadControlSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*LoadControlSuite)(nil) -func (s *LoadControlSuite) WriteSpineMessage(message []byte) { +func (s *LoadControlSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/features/measurement.go b/features/measurement.go index 253361c3..c5c2a288 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -9,7 +9,10 @@ type Measurement struct { *FeatureImpl } -func NewMeasurement(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*Measurement, error) { +func NewMeasurement( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*Measurement, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeMeasurement, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/measurement_test.go b/features/measurement_test.go index f969e587..b336cd79 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -20,16 +20,16 @@ func TestMeasurementSuite(t *testing.T) { type MeasurementSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface measurement *features.Measurement sentMessage []byte } -var _ shipapi.SpineDataConnection = (*MeasurementSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*MeasurementSuite)(nil) -func (s *MeasurementSuite) WriteSpineMessage(message []byte) { +func (s *MeasurementSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/features/timeseries.go b/features/timeseries.go index 1a0f5cc1..468d07b8 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -9,7 +9,10 @@ type TimeSeries struct { *FeatureImpl } -func NewTimeSeries(localRole, remoteRole model.RoleType, localEntity api.EntityLocal, remoteEntity api.EntityRemote) (*TimeSeries, error) { +func NewTimeSeries( + localRole, remoteRole model.RoleType, + localEntity api.EntityLocalInterface, + remoteEntity api.EntityRemoteInterface) (*TimeSeries, error) { feature, err := NewFeatureImpl(model.FeatureTypeTypeTimeSeries, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err diff --git a/features/timeseries_test.go b/features/timeseries_test.go index 06d9f971..ea7f9e33 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -20,16 +20,16 @@ func TestTimeSeriesSuite(t *testing.T) { type TimeSeriesSuite struct { suite.Suite - localEntity spineapi.EntityLocal - remoteEntity spineapi.EntityRemote + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface timeSeries *features.TimeSeries sentMessage []byte } -var _ shipapi.SpineDataConnection = (*TimeSeriesSuite)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*TimeSeriesSuite)(nil) -func (s *TimeSeriesSuite) WriteSpineMessage(message []byte) { +func (s *TimeSeriesSuite) WriteShipMessageWithPayload(message []byte) { s.sentMessage = message } diff --git a/go.mod b/go.mod index cae09d79..12e31d97 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec - github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41 + github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 + github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index ae77fdc3..bb0447b5 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec h1:Bu0hu9rsJIkUCtg64YJZte5OaGx0LStrmS10ufEma6o= -github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41 h1:FoeVeEesGzKoFL1YRD+DS65Jm5O2vXqXl5/5k2WSbxg= -github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41/go.mod h1:v/MAO6HzXJ8fzvWkC2454W+bHmPO8Ry7lvRj/eWmREQ= +github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 h1:I54U0+MHwtsHUS7OLEt0z5fGNZOpV7G3EgQxRvFXeKI= +github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f h1:BmUN9NM/mWblJIvvctkPgHN65/+ZmwyN1IF3a6Jzdy0= +github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f/go.mod h1:VkPWyFUVlf385o7NeesvAiYoDDDnYyLuIYYdhzfJ0pY= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= diff --git a/integration_tests/emobility_measurement_test.go b/integration_tests/emobility_measurement_test.go index c6cd8c2d..fc98cee3 100644 --- a/integration_tests/emobility_measurement_test.go +++ b/integration_tests/emobility_measurement_test.go @@ -25,27 +25,27 @@ func TestEmobilityMeasurementSuite(t *testing.T) { type EmobilityMeasurementSuite struct { suite.Suite - sut api.DeviceLocal - localEntity api.EntityLocal + sut api.DeviceLocalInterface + localEntity api.EntityLocalInterface measurement *features.Measurement electricalconnection *features.ElectricalConnection remoteSki string - remoteDevice api.DeviceRemote + remoteDevice api.DeviceRemoteInterface writeHandler *WriteMessageHandler } func (s *EmobilityMeasurementSuite) BeforeTest(suiteName, testName string) { - s.sut = spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", + s.sut = spine.NewDeviceLocal("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) - s.localEntity = spine.NewEntityLocalImpl(s.sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) + s.localEntity = spine.NewEntityLocal(s.sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) s.sut.AddEntity(s.localEntity) - f := spine.NewFeatureLocalImpl(1, s.localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + f := spine.NewFeatureLocal(1, s.localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) s.localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(2, s.localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + f = spine.NewFeatureLocal(2, s.localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) s.localEntity.AddFeature(f) s.remoteSki = "TestRemoteSki" diff --git a/integration_tests/helper_test.go b/integration_tests/helper_test.go index 64c30d01..566918d3 100644 --- a/integration_tests/helper_test.go +++ b/integration_tests/helper_test.go @@ -23,9 +23,9 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ shipapi.SpineDataConnection = (*WriteMessageHandler)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*WriteMessageHandler)(nil) -func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { +func (t *WriteMessageHandler) WriteShipMessageWithPayload(message []byte) { t.mux.Lock() defer t.mux.Unlock() @@ -95,7 +95,7 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg return nil } -func initialCommunication(t *testing.T, remoteDevice spineapi.DeviceRemote, writeHandler *WriteMessageHandler) { +func initialCommunication(t *testing.T, remoteDevice spineapi.DeviceRemoteInterface, writeHandler *WriteMessageHandler) { // Initial generic communication _, _ = remoteDevice.HandleSpineMesssage(loadFileData(t, wallbox_detaileddiscoverydata_recv_reply_file_path)) diff --git a/mocks/EEBUSService.go b/mocks/EEBUSService.go deleted file mode 100644 index d7a2293c..00000000 --- a/mocks/EEBUSService.go +++ /dev/null @@ -1,618 +0,0 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. - -package mocks - -import ( - api "github.com/enbility/eebus-go/api" - logging "github.com/enbility/ship-go/logging" - - mock "github.com/stretchr/testify/mock" - - ship_goapi "github.com/enbility/ship-go/api" - - spine_goapi "github.com/enbility/spine-go/api" -) - -// EEBUSService is an autogenerated mock type for the EEBUSService type -type EEBUSService struct { - mock.Mock -} - -type EEBUSService_Expecter struct { - mock *mock.Mock -} - -func (_m *EEBUSService) EXPECT() *EEBUSService_Expecter { - return &EEBUSService_Expecter{mock: &_m.Mock} -} - -// CancelPairingWithSKI provides a mock function with given fields: ski -func (_m *EEBUSService) CancelPairingWithSKI(ski string) { - _m.Called(ski) -} - -// EEBUSService_CancelPairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelPairingWithSKI' -type EEBUSService_CancelPairingWithSKI_Call struct { - *mock.Call -} - -// CancelPairingWithSKI is a helper method to define mock.On call -// - ski string -func (_e *EEBUSService_Expecter) CancelPairingWithSKI(ski interface{}) *EEBUSService_CancelPairingWithSKI_Call { - return &EEBUSService_CancelPairingWithSKI_Call{Call: _e.mock.On("CancelPairingWithSKI", ski)} -} - -func (_c *EEBUSService_CancelPairingWithSKI_Call) Run(run func(ski string)) *EEBUSService_CancelPairingWithSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *EEBUSService_CancelPairingWithSKI_Call) Return() *EEBUSService_CancelPairingWithSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_CancelPairingWithSKI_Call) RunAndReturn(run func(string)) *EEBUSService_CancelPairingWithSKI_Call { - _c.Call.Return(run) - return _c -} - -// Configuration provides a mock function with given fields: -func (_m *EEBUSService) Configuration() *api.Configuration { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Configuration") - } - - var r0 *api.Configuration - if rf, ok := ret.Get(0).(func() *api.Configuration); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*api.Configuration) - } - } - - return r0 -} - -// EEBUSService_Configuration_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Configuration' -type EEBUSService_Configuration_Call struct { - *mock.Call -} - -// Configuration is a helper method to define mock.On call -func (_e *EEBUSService_Expecter) Configuration() *EEBUSService_Configuration_Call { - return &EEBUSService_Configuration_Call{Call: _e.mock.On("Configuration")} -} - -func (_c *EEBUSService_Configuration_Call) Run(run func()) *EEBUSService_Configuration_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EEBUSService_Configuration_Call) Return(_a0 *api.Configuration) *EEBUSService_Configuration_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *EEBUSService_Configuration_Call) RunAndReturn(run func() *api.Configuration) *EEBUSService_Configuration_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectSKI provides a mock function with given fields: ski, reason -func (_m *EEBUSService) DisconnectSKI(ski string, reason string) { - _m.Called(ski, reason) -} - -// EEBUSService_DisconnectSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectSKI' -type EEBUSService_DisconnectSKI_Call struct { - *mock.Call -} - -// DisconnectSKI is a helper method to define mock.On call -// - ski string -// - reason string -func (_e *EEBUSService_Expecter) DisconnectSKI(ski interface{}, reason interface{}) *EEBUSService_DisconnectSKI_Call { - return &EEBUSService_DisconnectSKI_Call{Call: _e.mock.On("DisconnectSKI", ski, reason)} -} - -func (_c *EEBUSService_DisconnectSKI_Call) Run(run func(ski string, reason string)) *EEBUSService_DisconnectSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *EEBUSService_DisconnectSKI_Call) Return() *EEBUSService_DisconnectSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_DisconnectSKI_Call) RunAndReturn(run func(string, string)) *EEBUSService_DisconnectSKI_Call { - _c.Call.Return(run) - return _c -} - -// InitiatePairingWithSKI provides a mock function with given fields: ski -func (_m *EEBUSService) InitiatePairingWithSKI(ski string) { - _m.Called(ski) -} - -// EEBUSService_InitiatePairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitiatePairingWithSKI' -type EEBUSService_InitiatePairingWithSKI_Call struct { - *mock.Call -} - -// InitiatePairingWithSKI is a helper method to define mock.On call -// - ski string -func (_e *EEBUSService_Expecter) InitiatePairingWithSKI(ski interface{}) *EEBUSService_InitiatePairingWithSKI_Call { - return &EEBUSService_InitiatePairingWithSKI_Call{Call: _e.mock.On("InitiatePairingWithSKI", ski)} -} - -func (_c *EEBUSService_InitiatePairingWithSKI_Call) Run(run func(ski string)) *EEBUSService_InitiatePairingWithSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *EEBUSService_InitiatePairingWithSKI_Call) Return() *EEBUSService_InitiatePairingWithSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_InitiatePairingWithSKI_Call) RunAndReturn(run func(string)) *EEBUSService_InitiatePairingWithSKI_Call { - _c.Call.Return(run) - return _c -} - -// LocalDevice provides a mock function with given fields: -func (_m *EEBUSService) LocalDevice() spine_goapi.DeviceLocal { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for LocalDevice") - } - - var r0 spine_goapi.DeviceLocal - if rf, ok := ret.Get(0).(func() spine_goapi.DeviceLocal); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(spine_goapi.DeviceLocal) - } - } - - return r0 -} - -// EEBUSService_LocalDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalDevice' -type EEBUSService_LocalDevice_Call struct { - *mock.Call -} - -// LocalDevice is a helper method to define mock.On call -func (_e *EEBUSService_Expecter) LocalDevice() *EEBUSService_LocalDevice_Call { - return &EEBUSService_LocalDevice_Call{Call: _e.mock.On("LocalDevice")} -} - -func (_c *EEBUSService_LocalDevice_Call) Run(run func()) *EEBUSService_LocalDevice_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EEBUSService_LocalDevice_Call) Return(_a0 spine_goapi.DeviceLocal) *EEBUSService_LocalDevice_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *EEBUSService_LocalDevice_Call) RunAndReturn(run func() spine_goapi.DeviceLocal) *EEBUSService_LocalDevice_Call { - _c.Call.Return(run) - return _c -} - -// LocalService provides a mock function with given fields: -func (_m *EEBUSService) LocalService() *ship_goapi.ServiceDetails { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for LocalService") - } - - var r0 *ship_goapi.ServiceDetails - if rf, ok := ret.Get(0).(func() *ship_goapi.ServiceDetails); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ship_goapi.ServiceDetails) - } - } - - return r0 -} - -// EEBUSService_LocalService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalService' -type EEBUSService_LocalService_Call struct { - *mock.Call -} - -// LocalService is a helper method to define mock.On call -func (_e *EEBUSService_Expecter) LocalService() *EEBUSService_LocalService_Call { - return &EEBUSService_LocalService_Call{Call: _e.mock.On("LocalService")} -} - -func (_c *EEBUSService_LocalService_Call) Run(run func()) *EEBUSService_LocalService_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EEBUSService_LocalService_Call) Return(_a0 *ship_goapi.ServiceDetails) *EEBUSService_LocalService_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *EEBUSService_LocalService_Call) RunAndReturn(run func() *ship_goapi.ServiceDetails) *EEBUSService_LocalService_Call { - _c.Call.Return(run) - return _c -} - -// PairingDetailForSki provides a mock function with given fields: ski -func (_m *EEBUSService) PairingDetailForSki(ski string) *ship_goapi.ConnectionStateDetail { - ret := _m.Called(ski) - - if len(ret) == 0 { - panic("no return value specified for PairingDetailForSki") - } - - var r0 *ship_goapi.ConnectionStateDetail - if rf, ok := ret.Get(0).(func(string) *ship_goapi.ConnectionStateDetail); ok { - r0 = rf(ski) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ship_goapi.ConnectionStateDetail) - } - } - - return r0 -} - -// EEBUSService_PairingDetailForSki_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PairingDetailForSki' -type EEBUSService_PairingDetailForSki_Call struct { - *mock.Call -} - -// PairingDetailForSki is a helper method to define mock.On call -// - ski string -func (_e *EEBUSService_Expecter) PairingDetailForSki(ski interface{}) *EEBUSService_PairingDetailForSki_Call { - return &EEBUSService_PairingDetailForSki_Call{Call: _e.mock.On("PairingDetailForSki", ski)} -} - -func (_c *EEBUSService_PairingDetailForSki_Call) Run(run func(ski string)) *EEBUSService_PairingDetailForSki_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *EEBUSService_PairingDetailForSki_Call) Return(_a0 *ship_goapi.ConnectionStateDetail) *EEBUSService_PairingDetailForSki_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *EEBUSService_PairingDetailForSki_Call) RunAndReturn(run func(string) *ship_goapi.ConnectionStateDetail) *EEBUSService_PairingDetailForSki_Call { - _c.Call.Return(run) - return _c -} - -// RegisterRemoteSKI provides a mock function with given fields: ski, enable -func (_m *EEBUSService) RegisterRemoteSKI(ski string, enable bool) { - _m.Called(ski, enable) -} - -// EEBUSService_RegisterRemoteSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterRemoteSKI' -type EEBUSService_RegisterRemoteSKI_Call struct { - *mock.Call -} - -// RegisterRemoteSKI is a helper method to define mock.On call -// - ski string -// - enable bool -func (_e *EEBUSService_Expecter) RegisterRemoteSKI(ski interface{}, enable interface{}) *EEBUSService_RegisterRemoteSKI_Call { - return &EEBUSService_RegisterRemoteSKI_Call{Call: _e.mock.On("RegisterRemoteSKI", ski, enable)} -} - -func (_c *EEBUSService_RegisterRemoteSKI_Call) Run(run func(ski string, enable bool)) *EEBUSService_RegisterRemoteSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *EEBUSService_RegisterRemoteSKI_Call) Return() *EEBUSService_RegisterRemoteSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_RegisterRemoteSKI_Call) RunAndReturn(run func(string, bool)) *EEBUSService_RegisterRemoteSKI_Call { - _c.Call.Return(run) - return _c -} - -// RemoteServiceForSKI provides a mock function with given fields: ski -func (_m *EEBUSService) RemoteServiceForSKI(ski string) *ship_goapi.ServiceDetails { - ret := _m.Called(ski) - - if len(ret) == 0 { - panic("no return value specified for RemoteServiceForSKI") - } - - var r0 *ship_goapi.ServiceDetails - if rf, ok := ret.Get(0).(func(string) *ship_goapi.ServiceDetails); ok { - r0 = rf(ski) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ship_goapi.ServiceDetails) - } - } - - return r0 -} - -// EEBUSService_RemoteServiceForSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteServiceForSKI' -type EEBUSService_RemoteServiceForSKI_Call struct { - *mock.Call -} - -// RemoteServiceForSKI is a helper method to define mock.On call -// - ski string -func (_e *EEBUSService_Expecter) RemoteServiceForSKI(ski interface{}) *EEBUSService_RemoteServiceForSKI_Call { - return &EEBUSService_RemoteServiceForSKI_Call{Call: _e.mock.On("RemoteServiceForSKI", ski)} -} - -func (_c *EEBUSService_RemoteServiceForSKI_Call) Run(run func(ski string)) *EEBUSService_RemoteServiceForSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *EEBUSService_RemoteServiceForSKI_Call) Return(_a0 *ship_goapi.ServiceDetails) *EEBUSService_RemoteServiceForSKI_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *EEBUSService_RemoteServiceForSKI_Call) RunAndReturn(run func(string) *ship_goapi.ServiceDetails) *EEBUSService_RemoteServiceForSKI_Call { - _c.Call.Return(run) - return _c -} - -// SetLogging provides a mock function with given fields: logger -func (_m *EEBUSService) SetLogging(logger logging.Logging) { - _m.Called(logger) -} - -// EEBUSService_SetLogging_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetLogging' -type EEBUSService_SetLogging_Call struct { - *mock.Call -} - -// SetLogging is a helper method to define mock.On call -// - logger logging.Logging -func (_e *EEBUSService_Expecter) SetLogging(logger interface{}) *EEBUSService_SetLogging_Call { - return &EEBUSService_SetLogging_Call{Call: _e.mock.On("SetLogging", logger)} -} - -func (_c *EEBUSService_SetLogging_Call) Run(run func(logger logging.Logging)) *EEBUSService_SetLogging_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(logging.Logging)) - }) - return _c -} - -func (_c *EEBUSService_SetLogging_Call) Return() *EEBUSService_SetLogging_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_SetLogging_Call) RunAndReturn(run func(logging.Logging)) *EEBUSService_SetLogging_Call { - _c.Call.Return(run) - return _c -} - -// Setup provides a mock function with given fields: -func (_m *EEBUSService) Setup() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Setup") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// EEBUSService_Setup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Setup' -type EEBUSService_Setup_Call struct { - *mock.Call -} - -// Setup is a helper method to define mock.On call -func (_e *EEBUSService_Expecter) Setup() *EEBUSService_Setup_Call { - return &EEBUSService_Setup_Call{Call: _e.mock.On("Setup")} -} - -func (_c *EEBUSService_Setup_Call) Run(run func()) *EEBUSService_Setup_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EEBUSService_Setup_Call) Return(_a0 error) *EEBUSService_Setup_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *EEBUSService_Setup_Call) RunAndReturn(run func() error) *EEBUSService_Setup_Call { - _c.Call.Return(run) - return _c -} - -// Shutdown provides a mock function with given fields: -func (_m *EEBUSService) Shutdown() { - _m.Called() -} - -// EEBUSService_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown' -type EEBUSService_Shutdown_Call struct { - *mock.Call -} - -// Shutdown is a helper method to define mock.On call -func (_e *EEBUSService_Expecter) Shutdown() *EEBUSService_Shutdown_Call { - return &EEBUSService_Shutdown_Call{Call: _e.mock.On("Shutdown")} -} - -func (_c *EEBUSService_Shutdown_Call) Run(run func()) *EEBUSService_Shutdown_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EEBUSService_Shutdown_Call) Return() *EEBUSService_Shutdown_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_Shutdown_Call) RunAndReturn(run func()) *EEBUSService_Shutdown_Call { - _c.Call.Return(run) - return _c -} - -// Start provides a mock function with given fields: -func (_m *EEBUSService) Start() { - _m.Called() -} - -// EEBUSService_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type EEBUSService_Start_Call struct { - *mock.Call -} - -// Start is a helper method to define mock.On call -func (_e *EEBUSService_Expecter) Start() *EEBUSService_Start_Call { - return &EEBUSService_Start_Call{Call: _e.mock.On("Start")} -} - -func (_c *EEBUSService_Start_Call) Run(run func()) *EEBUSService_Start_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EEBUSService_Start_Call) Return() *EEBUSService_Start_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_Start_Call) RunAndReturn(run func()) *EEBUSService_Start_Call { - _c.Call.Return(run) - return _c -} - -// StartBrowseMdnsEntries provides a mock function with given fields: -func (_m *EEBUSService) StartBrowseMdnsEntries() { - _m.Called() -} - -// EEBUSService_StartBrowseMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartBrowseMdnsEntries' -type EEBUSService_StartBrowseMdnsEntries_Call struct { - *mock.Call -} - -// StartBrowseMdnsEntries is a helper method to define mock.On call -func (_e *EEBUSService_Expecter) StartBrowseMdnsEntries() *EEBUSService_StartBrowseMdnsEntries_Call { - return &EEBUSService_StartBrowseMdnsEntries_Call{Call: _e.mock.On("StartBrowseMdnsEntries")} -} - -func (_c *EEBUSService_StartBrowseMdnsEntries_Call) Run(run func()) *EEBUSService_StartBrowseMdnsEntries_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EEBUSService_StartBrowseMdnsEntries_Call) Return() *EEBUSService_StartBrowseMdnsEntries_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_StartBrowseMdnsEntries_Call) RunAndReturn(run func()) *EEBUSService_StartBrowseMdnsEntries_Call { - _c.Call.Return(run) - return _c -} - -// StopBrowseMdnsEntries provides a mock function with given fields: -func (_m *EEBUSService) StopBrowseMdnsEntries() { - _m.Called() -} - -// EEBUSService_StopBrowseMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StopBrowseMdnsEntries' -type EEBUSService_StopBrowseMdnsEntries_Call struct { - *mock.Call -} - -// StopBrowseMdnsEntries is a helper method to define mock.On call -func (_e *EEBUSService_Expecter) StopBrowseMdnsEntries() *EEBUSService_StopBrowseMdnsEntries_Call { - return &EEBUSService_StopBrowseMdnsEntries_Call{Call: _e.mock.On("StopBrowseMdnsEntries")} -} - -func (_c *EEBUSService_StopBrowseMdnsEntries_Call) Run(run func()) *EEBUSService_StopBrowseMdnsEntries_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EEBUSService_StopBrowseMdnsEntries_Call) Return() *EEBUSService_StopBrowseMdnsEntries_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSService_StopBrowseMdnsEntries_Call) RunAndReturn(run func()) *EEBUSService_StopBrowseMdnsEntries_Call { - _c.Call.Return(run) - return _c -} - -// NewEEBUSService creates a new instance of EEBUSService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewEEBUSService(t interface { - mock.TestingT - Cleanup(func()) -}) *EEBUSService { - mock := &EEBUSService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/mocks/EEBUSServiceHandler.go b/mocks/EEBUSServiceHandler.go deleted file mode 100644 index ff2872f8..00000000 --- a/mocks/EEBUSServiceHandler.go +++ /dev/null @@ -1,253 +0,0 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. - -package mocks - -import ( - api "github.com/enbility/eebus-go/api" - mock "github.com/stretchr/testify/mock" - - ship_goapi "github.com/enbility/ship-go/api" -) - -// EEBUSServiceHandler is an autogenerated mock type for the EEBUSServiceHandler type -type EEBUSServiceHandler struct { - mock.Mock -} - -type EEBUSServiceHandler_Expecter struct { - mock *mock.Mock -} - -func (_m *EEBUSServiceHandler) EXPECT() *EEBUSServiceHandler_Expecter { - return &EEBUSServiceHandler_Expecter{mock: &_m.Mock} -} - -// AllowWaitingForTrust provides a mock function with given fields: ski -func (_m *EEBUSServiceHandler) AllowWaitingForTrust(ski string) bool { - ret := _m.Called(ski) - - if len(ret) == 0 { - panic("no return value specified for AllowWaitingForTrust") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(string) bool); ok { - r0 = rf(ski) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// EEBUSServiceHandler_AllowWaitingForTrust_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AllowWaitingForTrust' -type EEBUSServiceHandler_AllowWaitingForTrust_Call struct { - *mock.Call -} - -// AllowWaitingForTrust is a helper method to define mock.On call -// - ski string -func (_e *EEBUSServiceHandler_Expecter) AllowWaitingForTrust(ski interface{}) *EEBUSServiceHandler_AllowWaitingForTrust_Call { - return &EEBUSServiceHandler_AllowWaitingForTrust_Call{Call: _e.mock.On("AllowWaitingForTrust", ski)} -} - -func (_c *EEBUSServiceHandler_AllowWaitingForTrust_Call) Run(run func(ski string)) *EEBUSServiceHandler_AllowWaitingForTrust_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *EEBUSServiceHandler_AllowWaitingForTrust_Call) Return(_a0 bool) *EEBUSServiceHandler_AllowWaitingForTrust_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *EEBUSServiceHandler_AllowWaitingForTrust_Call) RunAndReturn(run func(string) bool) *EEBUSServiceHandler_AllowWaitingForTrust_Call { - _c.Call.Return(run) - return _c -} - -// RemoteSKIConnected provides a mock function with given fields: service, ski -func (_m *EEBUSServiceHandler) RemoteSKIConnected(service api.EEBUSService, ski string) { - _m.Called(service, ski) -} - -// EEBUSServiceHandler_RemoteSKIConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIConnected' -type EEBUSServiceHandler_RemoteSKIConnected_Call struct { - *mock.Call -} - -// RemoteSKIConnected is a helper method to define mock.On call -// - service api.EEBUSService -// - ski string -func (_e *EEBUSServiceHandler_Expecter) RemoteSKIConnected(service interface{}, ski interface{}) *EEBUSServiceHandler_RemoteSKIConnected_Call { - return &EEBUSServiceHandler_RemoteSKIConnected_Call{Call: _e.mock.On("RemoteSKIConnected", service, ski)} -} - -func (_c *EEBUSServiceHandler_RemoteSKIConnected_Call) Run(run func(service api.EEBUSService, ski string)) *EEBUSServiceHandler_RemoteSKIConnected_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.EEBUSService), args[1].(string)) - }) - return _c -} - -func (_c *EEBUSServiceHandler_RemoteSKIConnected_Call) Return() *EEBUSServiceHandler_RemoteSKIConnected_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSServiceHandler_RemoteSKIConnected_Call) RunAndReturn(run func(api.EEBUSService, string)) *EEBUSServiceHandler_RemoteSKIConnected_Call { - _c.Call.Return(run) - return _c -} - -// RemoteSKIDisconnected provides a mock function with given fields: service, ski -func (_m *EEBUSServiceHandler) RemoteSKIDisconnected(service api.EEBUSService, ski string) { - _m.Called(service, ski) -} - -// EEBUSServiceHandler_RemoteSKIDisconnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIDisconnected' -type EEBUSServiceHandler_RemoteSKIDisconnected_Call struct { - *mock.Call -} - -// RemoteSKIDisconnected is a helper method to define mock.On call -// - service api.EEBUSService -// - ski string -func (_e *EEBUSServiceHandler_Expecter) RemoteSKIDisconnected(service interface{}, ski interface{}) *EEBUSServiceHandler_RemoteSKIDisconnected_Call { - return &EEBUSServiceHandler_RemoteSKIDisconnected_Call{Call: _e.mock.On("RemoteSKIDisconnected", service, ski)} -} - -func (_c *EEBUSServiceHandler_RemoteSKIDisconnected_Call) Run(run func(service api.EEBUSService, ski string)) *EEBUSServiceHandler_RemoteSKIDisconnected_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.EEBUSService), args[1].(string)) - }) - return _c -} - -func (_c *EEBUSServiceHandler_RemoteSKIDisconnected_Call) Return() *EEBUSServiceHandler_RemoteSKIDisconnected_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSServiceHandler_RemoteSKIDisconnected_Call) RunAndReturn(run func(api.EEBUSService, string)) *EEBUSServiceHandler_RemoteSKIDisconnected_Call { - _c.Call.Return(run) - return _c -} - -// ServicePairingDetailUpdate provides a mock function with given fields: ski, detail -func (_m *EEBUSServiceHandler) ServicePairingDetailUpdate(ski string, detail *ship_goapi.ConnectionStateDetail) { - _m.Called(ski, detail) -} - -// EEBUSServiceHandler_ServicePairingDetailUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServicePairingDetailUpdate' -type EEBUSServiceHandler_ServicePairingDetailUpdate_Call struct { - *mock.Call -} - -// ServicePairingDetailUpdate is a helper method to define mock.On call -// - ski string -// - detail *ship_goapi.ConnectionStateDetail -func (_e *EEBUSServiceHandler_Expecter) ServicePairingDetailUpdate(ski interface{}, detail interface{}) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { - return &EEBUSServiceHandler_ServicePairingDetailUpdate_Call{Call: _e.mock.On("ServicePairingDetailUpdate", ski, detail)} -} - -func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) Run(run func(ski string, detail *ship_goapi.ConnectionStateDetail)) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(*ship_goapi.ConnectionStateDetail)) - }) - return _c -} - -func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) Return() *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSServiceHandler_ServicePairingDetailUpdate_Call) RunAndReturn(run func(string, *ship_goapi.ConnectionStateDetail)) *EEBUSServiceHandler_ServicePairingDetailUpdate_Call { - _c.Call.Return(run) - return _c -} - -// ServiceShipIDUpdate provides a mock function with given fields: ski, shipdID -func (_m *EEBUSServiceHandler) ServiceShipIDUpdate(ski string, shipdID string) { - _m.Called(ski, shipdID) -} - -// EEBUSServiceHandler_ServiceShipIDUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServiceShipIDUpdate' -type EEBUSServiceHandler_ServiceShipIDUpdate_Call struct { - *mock.Call -} - -// ServiceShipIDUpdate is a helper method to define mock.On call -// - ski string -// - shipdID string -func (_e *EEBUSServiceHandler_Expecter) ServiceShipIDUpdate(ski interface{}, shipdID interface{}) *EEBUSServiceHandler_ServiceShipIDUpdate_Call { - return &EEBUSServiceHandler_ServiceShipIDUpdate_Call{Call: _e.mock.On("ServiceShipIDUpdate", ski, shipdID)} -} - -func (_c *EEBUSServiceHandler_ServiceShipIDUpdate_Call) Run(run func(ski string, shipdID string)) *EEBUSServiceHandler_ServiceShipIDUpdate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *EEBUSServiceHandler_ServiceShipIDUpdate_Call) Return() *EEBUSServiceHandler_ServiceShipIDUpdate_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSServiceHandler_ServiceShipIDUpdate_Call) RunAndReturn(run func(string, string)) *EEBUSServiceHandler_ServiceShipIDUpdate_Call { - _c.Call.Return(run) - return _c -} - -// VisibleRemoteServicesUpdated provides a mock function with given fields: service, entries -func (_m *EEBUSServiceHandler) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []ship_goapi.RemoteService) { - _m.Called(service, entries) -} - -// EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VisibleRemoteServicesUpdated' -type EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call struct { - *mock.Call -} - -// VisibleRemoteServicesUpdated is a helper method to define mock.On call -// - service api.EEBUSService -// - entries []ship_goapi.RemoteService -func (_e *EEBUSServiceHandler_Expecter) VisibleRemoteServicesUpdated(service interface{}, entries interface{}) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { - return &EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call{Call: _e.mock.On("VisibleRemoteServicesUpdated", service, entries)} -} - -func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) Run(run func(service api.EEBUSService, entries []ship_goapi.RemoteService)) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.EEBUSService), args[1].([]ship_goapi.RemoteService)) - }) - return _c -} - -func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) Return() *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { - _c.Call.Return() - return _c -} - -func (_c *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call) RunAndReturn(run func(api.EEBUSService, []ship_goapi.RemoteService)) *EEBUSServiceHandler_VisibleRemoteServicesUpdated_Call { - _c.Call.Return(run) - return _c -} - -// NewEEBUSServiceHandler creates a new instance of EEBUSServiceHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewEEBUSServiceHandler(t interface { - mock.TestingT - Cleanup(func()) -}) *EEBUSServiceHandler { - mock := &EEBUSServiceHandler{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/mocks/ServiceInterface.go b/mocks/ServiceInterface.go new file mode 100644 index 00000000..2456b842 --- /dev/null +++ b/mocks/ServiceInterface.go @@ -0,0 +1,618 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/eebus-go/api" + logging "github.com/enbility/ship-go/logging" + + mock "github.com/stretchr/testify/mock" + + ship_goapi "github.com/enbility/ship-go/api" + + spine_goapi "github.com/enbility/spine-go/api" +) + +// ServiceInterface is an autogenerated mock type for the ServiceInterface type +type ServiceInterface struct { + mock.Mock +} + +type ServiceInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *ServiceInterface) EXPECT() *ServiceInterface_Expecter { + return &ServiceInterface_Expecter{mock: &_m.Mock} +} + +// CancelPairingWithSKI provides a mock function with given fields: ski +func (_m *ServiceInterface) CancelPairingWithSKI(ski string) { + _m.Called(ski) +} + +// ServiceInterface_CancelPairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelPairingWithSKI' +type ServiceInterface_CancelPairingWithSKI_Call struct { + *mock.Call +} + +// CancelPairingWithSKI is a helper method to define mock.On call +// - ski string +func (_e *ServiceInterface_Expecter) CancelPairingWithSKI(ski interface{}) *ServiceInterface_CancelPairingWithSKI_Call { + return &ServiceInterface_CancelPairingWithSKI_Call{Call: _e.mock.On("CancelPairingWithSKI", ski)} +} + +func (_c *ServiceInterface_CancelPairingWithSKI_Call) Run(run func(ski string)) *ServiceInterface_CancelPairingWithSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceInterface_CancelPairingWithSKI_Call) Return() *ServiceInterface_CancelPairingWithSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_CancelPairingWithSKI_Call) RunAndReturn(run func(string)) *ServiceInterface_CancelPairingWithSKI_Call { + _c.Call.Return(run) + return _c +} + +// Configuration provides a mock function with given fields: +func (_m *ServiceInterface) Configuration() *api.Configuration { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Configuration") + } + + var r0 *api.Configuration + if rf, ok := ret.Get(0).(func() *api.Configuration); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*api.Configuration) + } + } + + return r0 +} + +// ServiceInterface_Configuration_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Configuration' +type ServiceInterface_Configuration_Call struct { + *mock.Call +} + +// Configuration is a helper method to define mock.On call +func (_e *ServiceInterface_Expecter) Configuration() *ServiceInterface_Configuration_Call { + return &ServiceInterface_Configuration_Call{Call: _e.mock.On("Configuration")} +} + +func (_c *ServiceInterface_Configuration_Call) Run(run func()) *ServiceInterface_Configuration_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServiceInterface_Configuration_Call) Return(_a0 *api.Configuration) *ServiceInterface_Configuration_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServiceInterface_Configuration_Call) RunAndReturn(run func() *api.Configuration) *ServiceInterface_Configuration_Call { + _c.Call.Return(run) + return _c +} + +// DisconnectSKI provides a mock function with given fields: ski, reason +func (_m *ServiceInterface) DisconnectSKI(ski string, reason string) { + _m.Called(ski, reason) +} + +// ServiceInterface_DisconnectSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectSKI' +type ServiceInterface_DisconnectSKI_Call struct { + *mock.Call +} + +// DisconnectSKI is a helper method to define mock.On call +// - ski string +// - reason string +func (_e *ServiceInterface_Expecter) DisconnectSKI(ski interface{}, reason interface{}) *ServiceInterface_DisconnectSKI_Call { + return &ServiceInterface_DisconnectSKI_Call{Call: _e.mock.On("DisconnectSKI", ski, reason)} +} + +func (_c *ServiceInterface_DisconnectSKI_Call) Run(run func(ski string, reason string)) *ServiceInterface_DisconnectSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *ServiceInterface_DisconnectSKI_Call) Return() *ServiceInterface_DisconnectSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_DisconnectSKI_Call) RunAndReturn(run func(string, string)) *ServiceInterface_DisconnectSKI_Call { + _c.Call.Return(run) + return _c +} + +// InitiatePairingWithSKI provides a mock function with given fields: ski +func (_m *ServiceInterface) InitiatePairingWithSKI(ski string) { + _m.Called(ski) +} + +// ServiceInterface_InitiatePairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitiatePairingWithSKI' +type ServiceInterface_InitiatePairingWithSKI_Call struct { + *mock.Call +} + +// InitiatePairingWithSKI is a helper method to define mock.On call +// - ski string +func (_e *ServiceInterface_Expecter) InitiatePairingWithSKI(ski interface{}) *ServiceInterface_InitiatePairingWithSKI_Call { + return &ServiceInterface_InitiatePairingWithSKI_Call{Call: _e.mock.On("InitiatePairingWithSKI", ski)} +} + +func (_c *ServiceInterface_InitiatePairingWithSKI_Call) Run(run func(ski string)) *ServiceInterface_InitiatePairingWithSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceInterface_InitiatePairingWithSKI_Call) Return() *ServiceInterface_InitiatePairingWithSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_InitiatePairingWithSKI_Call) RunAndReturn(run func(string)) *ServiceInterface_InitiatePairingWithSKI_Call { + _c.Call.Return(run) + return _c +} + +// LocalDevice provides a mock function with given fields: +func (_m *ServiceInterface) LocalDevice() spine_goapi.DeviceLocalInterface { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LocalDevice") + } + + var r0 spine_goapi.DeviceLocalInterface + if rf, ok := ret.Get(0).(func() spine_goapi.DeviceLocalInterface); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(spine_goapi.DeviceLocalInterface) + } + } + + return r0 +} + +// ServiceInterface_LocalDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalDevice' +type ServiceInterface_LocalDevice_Call struct { + *mock.Call +} + +// LocalDevice is a helper method to define mock.On call +func (_e *ServiceInterface_Expecter) LocalDevice() *ServiceInterface_LocalDevice_Call { + return &ServiceInterface_LocalDevice_Call{Call: _e.mock.On("LocalDevice")} +} + +func (_c *ServiceInterface_LocalDevice_Call) Run(run func()) *ServiceInterface_LocalDevice_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServiceInterface_LocalDevice_Call) Return(_a0 spine_goapi.DeviceLocalInterface) *ServiceInterface_LocalDevice_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServiceInterface_LocalDevice_Call) RunAndReturn(run func() spine_goapi.DeviceLocalInterface) *ServiceInterface_LocalDevice_Call { + _c.Call.Return(run) + return _c +} + +// LocalService provides a mock function with given fields: +func (_m *ServiceInterface) LocalService() *ship_goapi.ServiceDetails { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LocalService") + } + + var r0 *ship_goapi.ServiceDetails + if rf, ok := ret.Get(0).(func() *ship_goapi.ServiceDetails); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ship_goapi.ServiceDetails) + } + } + + return r0 +} + +// ServiceInterface_LocalService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalService' +type ServiceInterface_LocalService_Call struct { + *mock.Call +} + +// LocalService is a helper method to define mock.On call +func (_e *ServiceInterface_Expecter) LocalService() *ServiceInterface_LocalService_Call { + return &ServiceInterface_LocalService_Call{Call: _e.mock.On("LocalService")} +} + +func (_c *ServiceInterface_LocalService_Call) Run(run func()) *ServiceInterface_LocalService_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServiceInterface_LocalService_Call) Return(_a0 *ship_goapi.ServiceDetails) *ServiceInterface_LocalService_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServiceInterface_LocalService_Call) RunAndReturn(run func() *ship_goapi.ServiceDetails) *ServiceInterface_LocalService_Call { + _c.Call.Return(run) + return _c +} + +// PairingDetailForSki provides a mock function with given fields: ski +func (_m *ServiceInterface) PairingDetailForSki(ski string) *ship_goapi.ConnectionStateDetail { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for PairingDetailForSki") + } + + var r0 *ship_goapi.ConnectionStateDetail + if rf, ok := ret.Get(0).(func(string) *ship_goapi.ConnectionStateDetail); ok { + r0 = rf(ski) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ship_goapi.ConnectionStateDetail) + } + } + + return r0 +} + +// ServiceInterface_PairingDetailForSki_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PairingDetailForSki' +type ServiceInterface_PairingDetailForSki_Call struct { + *mock.Call +} + +// PairingDetailForSki is a helper method to define mock.On call +// - ski string +func (_e *ServiceInterface_Expecter) PairingDetailForSki(ski interface{}) *ServiceInterface_PairingDetailForSki_Call { + return &ServiceInterface_PairingDetailForSki_Call{Call: _e.mock.On("PairingDetailForSki", ski)} +} + +func (_c *ServiceInterface_PairingDetailForSki_Call) Run(run func(ski string)) *ServiceInterface_PairingDetailForSki_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceInterface_PairingDetailForSki_Call) Return(_a0 *ship_goapi.ConnectionStateDetail) *ServiceInterface_PairingDetailForSki_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServiceInterface_PairingDetailForSki_Call) RunAndReturn(run func(string) *ship_goapi.ConnectionStateDetail) *ServiceInterface_PairingDetailForSki_Call { + _c.Call.Return(run) + return _c +} + +// RegisterRemoteSKI provides a mock function with given fields: ski, enable +func (_m *ServiceInterface) RegisterRemoteSKI(ski string, enable bool) { + _m.Called(ski, enable) +} + +// ServiceInterface_RegisterRemoteSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterRemoteSKI' +type ServiceInterface_RegisterRemoteSKI_Call struct { + *mock.Call +} + +// RegisterRemoteSKI is a helper method to define mock.On call +// - ski string +// - enable bool +func (_e *ServiceInterface_Expecter) RegisterRemoteSKI(ski interface{}, enable interface{}) *ServiceInterface_RegisterRemoteSKI_Call { + return &ServiceInterface_RegisterRemoteSKI_Call{Call: _e.mock.On("RegisterRemoteSKI", ski, enable)} +} + +func (_c *ServiceInterface_RegisterRemoteSKI_Call) Run(run func(ski string, enable bool)) *ServiceInterface_RegisterRemoteSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(bool)) + }) + return _c +} + +func (_c *ServiceInterface_RegisterRemoteSKI_Call) Return() *ServiceInterface_RegisterRemoteSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_RegisterRemoteSKI_Call) RunAndReturn(run func(string, bool)) *ServiceInterface_RegisterRemoteSKI_Call { + _c.Call.Return(run) + return _c +} + +// RemoteServiceForSKI provides a mock function with given fields: ski +func (_m *ServiceInterface) RemoteServiceForSKI(ski string) *ship_goapi.ServiceDetails { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for RemoteServiceForSKI") + } + + var r0 *ship_goapi.ServiceDetails + if rf, ok := ret.Get(0).(func(string) *ship_goapi.ServiceDetails); ok { + r0 = rf(ski) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ship_goapi.ServiceDetails) + } + } + + return r0 +} + +// ServiceInterface_RemoteServiceForSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteServiceForSKI' +type ServiceInterface_RemoteServiceForSKI_Call struct { + *mock.Call +} + +// RemoteServiceForSKI is a helper method to define mock.On call +// - ski string +func (_e *ServiceInterface_Expecter) RemoteServiceForSKI(ski interface{}) *ServiceInterface_RemoteServiceForSKI_Call { + return &ServiceInterface_RemoteServiceForSKI_Call{Call: _e.mock.On("RemoteServiceForSKI", ski)} +} + +func (_c *ServiceInterface_RemoteServiceForSKI_Call) Run(run func(ski string)) *ServiceInterface_RemoteServiceForSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceInterface_RemoteServiceForSKI_Call) Return(_a0 *ship_goapi.ServiceDetails) *ServiceInterface_RemoteServiceForSKI_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServiceInterface_RemoteServiceForSKI_Call) RunAndReturn(run func(string) *ship_goapi.ServiceDetails) *ServiceInterface_RemoteServiceForSKI_Call { + _c.Call.Return(run) + return _c +} + +// SetLogging provides a mock function with given fields: logger +func (_m *ServiceInterface) SetLogging(logger logging.LoggingInterface) { + _m.Called(logger) +} + +// ServiceInterface_SetLogging_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetLogging' +type ServiceInterface_SetLogging_Call struct { + *mock.Call +} + +// SetLogging is a helper method to define mock.On call +// - logger logging.LoggingInterface +func (_e *ServiceInterface_Expecter) SetLogging(logger interface{}) *ServiceInterface_SetLogging_Call { + return &ServiceInterface_SetLogging_Call{Call: _e.mock.On("SetLogging", logger)} +} + +func (_c *ServiceInterface_SetLogging_Call) Run(run func(logger logging.LoggingInterface)) *ServiceInterface_SetLogging_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(logging.LoggingInterface)) + }) + return _c +} + +func (_c *ServiceInterface_SetLogging_Call) Return() *ServiceInterface_SetLogging_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_SetLogging_Call) RunAndReturn(run func(logging.LoggingInterface)) *ServiceInterface_SetLogging_Call { + _c.Call.Return(run) + return _c +} + +// Setup provides a mock function with given fields: +func (_m *ServiceInterface) Setup() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Setup") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServiceInterface_Setup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Setup' +type ServiceInterface_Setup_Call struct { + *mock.Call +} + +// Setup is a helper method to define mock.On call +func (_e *ServiceInterface_Expecter) Setup() *ServiceInterface_Setup_Call { + return &ServiceInterface_Setup_Call{Call: _e.mock.On("Setup")} +} + +func (_c *ServiceInterface_Setup_Call) Run(run func()) *ServiceInterface_Setup_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServiceInterface_Setup_Call) Return(_a0 error) *ServiceInterface_Setup_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServiceInterface_Setup_Call) RunAndReturn(run func() error) *ServiceInterface_Setup_Call { + _c.Call.Return(run) + return _c +} + +// Shutdown provides a mock function with given fields: +func (_m *ServiceInterface) Shutdown() { + _m.Called() +} + +// ServiceInterface_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown' +type ServiceInterface_Shutdown_Call struct { + *mock.Call +} + +// Shutdown is a helper method to define mock.On call +func (_e *ServiceInterface_Expecter) Shutdown() *ServiceInterface_Shutdown_Call { + return &ServiceInterface_Shutdown_Call{Call: _e.mock.On("Shutdown")} +} + +func (_c *ServiceInterface_Shutdown_Call) Run(run func()) *ServiceInterface_Shutdown_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServiceInterface_Shutdown_Call) Return() *ServiceInterface_Shutdown_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_Shutdown_Call) RunAndReturn(run func()) *ServiceInterface_Shutdown_Call { + _c.Call.Return(run) + return _c +} + +// Start provides a mock function with given fields: +func (_m *ServiceInterface) Start() { + _m.Called() +} + +// ServiceInterface_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' +type ServiceInterface_Start_Call struct { + *mock.Call +} + +// Start is a helper method to define mock.On call +func (_e *ServiceInterface_Expecter) Start() *ServiceInterface_Start_Call { + return &ServiceInterface_Start_Call{Call: _e.mock.On("Start")} +} + +func (_c *ServiceInterface_Start_Call) Run(run func()) *ServiceInterface_Start_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServiceInterface_Start_Call) Return() *ServiceInterface_Start_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_Start_Call) RunAndReturn(run func()) *ServiceInterface_Start_Call { + _c.Call.Return(run) + return _c +} + +// StartBrowseMdnsEntries provides a mock function with given fields: +func (_m *ServiceInterface) StartBrowseMdnsEntries() { + _m.Called() +} + +// ServiceInterface_StartBrowseMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartBrowseMdnsEntries' +type ServiceInterface_StartBrowseMdnsEntries_Call struct { + *mock.Call +} + +// StartBrowseMdnsEntries is a helper method to define mock.On call +func (_e *ServiceInterface_Expecter) StartBrowseMdnsEntries() *ServiceInterface_StartBrowseMdnsEntries_Call { + return &ServiceInterface_StartBrowseMdnsEntries_Call{Call: _e.mock.On("StartBrowseMdnsEntries")} +} + +func (_c *ServiceInterface_StartBrowseMdnsEntries_Call) Run(run func()) *ServiceInterface_StartBrowseMdnsEntries_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServiceInterface_StartBrowseMdnsEntries_Call) Return() *ServiceInterface_StartBrowseMdnsEntries_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_StartBrowseMdnsEntries_Call) RunAndReturn(run func()) *ServiceInterface_StartBrowseMdnsEntries_Call { + _c.Call.Return(run) + return _c +} + +// StopBrowseMdnsEntries provides a mock function with given fields: +func (_m *ServiceInterface) StopBrowseMdnsEntries() { + _m.Called() +} + +// ServiceInterface_StopBrowseMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StopBrowseMdnsEntries' +type ServiceInterface_StopBrowseMdnsEntries_Call struct { + *mock.Call +} + +// StopBrowseMdnsEntries is a helper method to define mock.On call +func (_e *ServiceInterface_Expecter) StopBrowseMdnsEntries() *ServiceInterface_StopBrowseMdnsEntries_Call { + return &ServiceInterface_StopBrowseMdnsEntries_Call{Call: _e.mock.On("StopBrowseMdnsEntries")} +} + +func (_c *ServiceInterface_StopBrowseMdnsEntries_Call) Run(run func()) *ServiceInterface_StopBrowseMdnsEntries_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServiceInterface_StopBrowseMdnsEntries_Call) Return() *ServiceInterface_StopBrowseMdnsEntries_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_StopBrowseMdnsEntries_Call) RunAndReturn(run func()) *ServiceInterface_StopBrowseMdnsEntries_Call { + _c.Call.Return(run) + return _c +} + +// NewServiceInterface creates a new instance of ServiceInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServiceInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *ServiceInterface { + mock := &ServiceInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/ServiceReaderInterface.go b/mocks/ServiceReaderInterface.go new file mode 100644 index 00000000..963f2a71 --- /dev/null +++ b/mocks/ServiceReaderInterface.go @@ -0,0 +1,253 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/eebus-go/api" + mock "github.com/stretchr/testify/mock" + + ship_goapi "github.com/enbility/ship-go/api" +) + +// ServiceReaderInterface is an autogenerated mock type for the ServiceReaderInterface type +type ServiceReaderInterface struct { + mock.Mock +} + +type ServiceReaderInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *ServiceReaderInterface) EXPECT() *ServiceReaderInterface_Expecter { + return &ServiceReaderInterface_Expecter{mock: &_m.Mock} +} + +// AllowWaitingForTrust provides a mock function with given fields: ski +func (_m *ServiceReaderInterface) AllowWaitingForTrust(ski string) bool { + ret := _m.Called(ski) + + if len(ret) == 0 { + panic("no return value specified for AllowWaitingForTrust") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(ski) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// ServiceReaderInterface_AllowWaitingForTrust_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AllowWaitingForTrust' +type ServiceReaderInterface_AllowWaitingForTrust_Call struct { + *mock.Call +} + +// AllowWaitingForTrust is a helper method to define mock.On call +// - ski string +func (_e *ServiceReaderInterface_Expecter) AllowWaitingForTrust(ski interface{}) *ServiceReaderInterface_AllowWaitingForTrust_Call { + return &ServiceReaderInterface_AllowWaitingForTrust_Call{Call: _e.mock.On("AllowWaitingForTrust", ski)} +} + +func (_c *ServiceReaderInterface_AllowWaitingForTrust_Call) Run(run func(ski string)) *ServiceReaderInterface_AllowWaitingForTrust_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceReaderInterface_AllowWaitingForTrust_Call) Return(_a0 bool) *ServiceReaderInterface_AllowWaitingForTrust_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServiceReaderInterface_AllowWaitingForTrust_Call) RunAndReturn(run func(string) bool) *ServiceReaderInterface_AllowWaitingForTrust_Call { + _c.Call.Return(run) + return _c +} + +// RemoteSKIConnected provides a mock function with given fields: service, ski +func (_m *ServiceReaderInterface) RemoteSKIConnected(service api.ServiceInterface, ski string) { + _m.Called(service, ski) +} + +// ServiceReaderInterface_RemoteSKIConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIConnected' +type ServiceReaderInterface_RemoteSKIConnected_Call struct { + *mock.Call +} + +// RemoteSKIConnected is a helper method to define mock.On call +// - service api.ServiceInterface +// - ski string +func (_e *ServiceReaderInterface_Expecter) RemoteSKIConnected(service interface{}, ski interface{}) *ServiceReaderInterface_RemoteSKIConnected_Call { + return &ServiceReaderInterface_RemoteSKIConnected_Call{Call: _e.mock.On("RemoteSKIConnected", service, ski)} +} + +func (_c *ServiceReaderInterface_RemoteSKIConnected_Call) Run(run func(service api.ServiceInterface, ski string)) *ServiceReaderInterface_RemoteSKIConnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.ServiceInterface), args[1].(string)) + }) + return _c +} + +func (_c *ServiceReaderInterface_RemoteSKIConnected_Call) Return() *ServiceReaderInterface_RemoteSKIConnected_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceReaderInterface_RemoteSKIConnected_Call) RunAndReturn(run func(api.ServiceInterface, string)) *ServiceReaderInterface_RemoteSKIConnected_Call { + _c.Call.Return(run) + return _c +} + +// RemoteSKIDisconnected provides a mock function with given fields: service, ski +func (_m *ServiceReaderInterface) RemoteSKIDisconnected(service api.ServiceInterface, ski string) { + _m.Called(service, ski) +} + +// ServiceReaderInterface_RemoteSKIDisconnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteSKIDisconnected' +type ServiceReaderInterface_RemoteSKIDisconnected_Call struct { + *mock.Call +} + +// RemoteSKIDisconnected is a helper method to define mock.On call +// - service api.ServiceInterface +// - ski string +func (_e *ServiceReaderInterface_Expecter) RemoteSKIDisconnected(service interface{}, ski interface{}) *ServiceReaderInterface_RemoteSKIDisconnected_Call { + return &ServiceReaderInterface_RemoteSKIDisconnected_Call{Call: _e.mock.On("RemoteSKIDisconnected", service, ski)} +} + +func (_c *ServiceReaderInterface_RemoteSKIDisconnected_Call) Run(run func(service api.ServiceInterface, ski string)) *ServiceReaderInterface_RemoteSKIDisconnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.ServiceInterface), args[1].(string)) + }) + return _c +} + +func (_c *ServiceReaderInterface_RemoteSKIDisconnected_Call) Return() *ServiceReaderInterface_RemoteSKIDisconnected_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceReaderInterface_RemoteSKIDisconnected_Call) RunAndReturn(run func(api.ServiceInterface, string)) *ServiceReaderInterface_RemoteSKIDisconnected_Call { + _c.Call.Return(run) + return _c +} + +// ServicePairingDetailUpdate provides a mock function with given fields: ski, detail +func (_m *ServiceReaderInterface) ServicePairingDetailUpdate(ski string, detail *ship_goapi.ConnectionStateDetail) { + _m.Called(ski, detail) +} + +// ServiceReaderInterface_ServicePairingDetailUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServicePairingDetailUpdate' +type ServiceReaderInterface_ServicePairingDetailUpdate_Call struct { + *mock.Call +} + +// ServicePairingDetailUpdate is a helper method to define mock.On call +// - ski string +// - detail *ship_goapi.ConnectionStateDetail +func (_e *ServiceReaderInterface_Expecter) ServicePairingDetailUpdate(ski interface{}, detail interface{}) *ServiceReaderInterface_ServicePairingDetailUpdate_Call { + return &ServiceReaderInterface_ServicePairingDetailUpdate_Call{Call: _e.mock.On("ServicePairingDetailUpdate", ski, detail)} +} + +func (_c *ServiceReaderInterface_ServicePairingDetailUpdate_Call) Run(run func(ski string, detail *ship_goapi.ConnectionStateDetail)) *ServiceReaderInterface_ServicePairingDetailUpdate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(*ship_goapi.ConnectionStateDetail)) + }) + return _c +} + +func (_c *ServiceReaderInterface_ServicePairingDetailUpdate_Call) Return() *ServiceReaderInterface_ServicePairingDetailUpdate_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceReaderInterface_ServicePairingDetailUpdate_Call) RunAndReturn(run func(string, *ship_goapi.ConnectionStateDetail)) *ServiceReaderInterface_ServicePairingDetailUpdate_Call { + _c.Call.Return(run) + return _c +} + +// ServiceShipIDUpdate provides a mock function with given fields: ski, shipdID +func (_m *ServiceReaderInterface) ServiceShipIDUpdate(ski string, shipdID string) { + _m.Called(ski, shipdID) +} + +// ServiceReaderInterface_ServiceShipIDUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServiceShipIDUpdate' +type ServiceReaderInterface_ServiceShipIDUpdate_Call struct { + *mock.Call +} + +// ServiceShipIDUpdate is a helper method to define mock.On call +// - ski string +// - shipdID string +func (_e *ServiceReaderInterface_Expecter) ServiceShipIDUpdate(ski interface{}, shipdID interface{}) *ServiceReaderInterface_ServiceShipIDUpdate_Call { + return &ServiceReaderInterface_ServiceShipIDUpdate_Call{Call: _e.mock.On("ServiceShipIDUpdate", ski, shipdID)} +} + +func (_c *ServiceReaderInterface_ServiceShipIDUpdate_Call) Run(run func(ski string, shipdID string)) *ServiceReaderInterface_ServiceShipIDUpdate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *ServiceReaderInterface_ServiceShipIDUpdate_Call) Return() *ServiceReaderInterface_ServiceShipIDUpdate_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceReaderInterface_ServiceShipIDUpdate_Call) RunAndReturn(run func(string, string)) *ServiceReaderInterface_ServiceShipIDUpdate_Call { + _c.Call.Return(run) + return _c +} + +// VisibleRemoteServicesUpdated provides a mock function with given fields: service, entries +func (_m *ServiceReaderInterface) VisibleRemoteServicesUpdated(service api.ServiceInterface, entries []ship_goapi.RemoteService) { + _m.Called(service, entries) +} + +// ServiceReaderInterface_VisibleRemoteServicesUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VisibleRemoteServicesUpdated' +type ServiceReaderInterface_VisibleRemoteServicesUpdated_Call struct { + *mock.Call +} + +// VisibleRemoteServicesUpdated is a helper method to define mock.On call +// - service api.ServiceInterface +// - entries []ship_goapi.RemoteService +func (_e *ServiceReaderInterface_Expecter) VisibleRemoteServicesUpdated(service interface{}, entries interface{}) *ServiceReaderInterface_VisibleRemoteServicesUpdated_Call { + return &ServiceReaderInterface_VisibleRemoteServicesUpdated_Call{Call: _e.mock.On("VisibleRemoteServicesUpdated", service, entries)} +} + +func (_c *ServiceReaderInterface_VisibleRemoteServicesUpdated_Call) Run(run func(service api.ServiceInterface, entries []ship_goapi.RemoteService)) *ServiceReaderInterface_VisibleRemoteServicesUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.ServiceInterface), args[1].([]ship_goapi.RemoteService)) + }) + return _c +} + +func (_c *ServiceReaderInterface_VisibleRemoteServicesUpdated_Call) Return() *ServiceReaderInterface_VisibleRemoteServicesUpdated_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceReaderInterface_VisibleRemoteServicesUpdated_Call) RunAndReturn(run func(api.ServiceInterface, []ship_goapi.RemoteService)) *ServiceReaderInterface_VisibleRemoteServicesUpdated_Call { + _c.Call.Return(run) + return _c +} + +// NewServiceReaderInterface creates a new instance of ServiceReaderInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServiceReaderInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *ServiceReaderInterface { + mock := &ServiceReaderInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/service/service.go b/service/service.go index 49c47cef..3aec1add 100644 --- a/service/service.go +++ b/service/service.go @@ -19,34 +19,34 @@ import ( // A service is the central element of an EEBUS service // including its websocket server and a zeroconf service. -type EEBUSServiceImpl struct { +type Service struct { configuration *api.Configuration // The local service details localService *shipapi.ServiceDetails // Connection Registrations - connectionsHub shipapi.Hub + connectionsHub shipapi.HubInterface // The SPINE specific device definition - spineLocalDevice spineapi.DeviceLocal + spineLocalDevice spineapi.DeviceLocalInterface - serviceHandler api.EEBUSServiceHandler + serviceHandler api.ServiceReaderInterface startOnce sync.Once } // creates a new EEBUS service -func NewEEBUSService(configuration *api.Configuration, serviceHandler api.EEBUSServiceHandler) *EEBUSServiceImpl { - return &EEBUSServiceImpl{ +func NewService(configuration *api.Configuration, serviceHandler api.ServiceReaderInterface) *Service { + return &Service{ configuration: configuration, serviceHandler: serviceHandler, } } -var _ shipapi.HubConnection = (*EEBUSServiceImpl)(nil) +var _ shipapi.HubReaderInterface = (*Service)(nil) -func (s *EEBUSServiceImpl) VisibleMDNSRecordsUpdated(entries []*shipapi.MdnsEntry) { +func (s *Service) VisibleMDNSRecordsUpdated(entries []*shipapi.MdnsEntry) { var remoteServices []shipapi.RemoteService for _, entry := range entries { @@ -65,12 +65,12 @@ func (s *EEBUSServiceImpl) VisibleMDNSRecordsUpdated(entries []*shipapi.MdnsEntr } // report a connection to a SKI -func (s *EEBUSServiceImpl) RemoteSKIConnected(ski string) { +func (s *Service) RemoteSKIConnected(ski string) { s.serviceHandler.RemoteSKIConnected(s, ski) } // report a disconnection to a SKI -func (s *EEBUSServiceImpl) RemoteSKIDisconnected(ski string) { +func (s *Service) RemoteSKIDisconnected(ski string) { if s.spineLocalDevice != nil { s.spineLocalDevice.RemoveRemoteDeviceConnection(ski) } @@ -79,46 +79,46 @@ func (s *EEBUSServiceImpl) RemoteSKIDisconnected(ski string) { } // Provides the SHIP ID the remote service reported during the handshake process -func (s *EEBUSServiceImpl) ServiceShipIDUpdate(ski string, shipdID string) { +func (s *Service) ServiceShipIDUpdate(ski string, shipdID string) { s.serviceHandler.ServiceShipIDUpdate(ski, shipdID) } // Provides the current pairing state for the remote service // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process -func (s *EEBUSServiceImpl) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { +func (s *Service) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { s.serviceHandler.ServicePairingDetailUpdate(ski, detail) } -func (s *EEBUSServiceImpl) SetupRemoteDevice(ski string, writeI shipapi.SpineDataConnection) shipapi.SpineDataProcessing { +func (s *Service) SetupRemoteDevice(ski string, writeI shipapi.ShipConnectionDataWriterInterface) shipapi.ShipConnectionDataReaderInterface { return s.LocalDevice().SetupRemoteDevice(ski, writeI) } // return if the user is still able to trust the connection -func (s *EEBUSServiceImpl) AllowWaitingForTrust(ski string) bool { +func (s *Service) AllowWaitingForTrust(ski string) bool { return s.serviceHandler.AllowWaitingForTrust(ski) } -var _ api.EEBUSService = (*EEBUSServiceImpl)(nil) +var _ api.ServiceInterface = (*Service)(nil) // Get the current pairing details for a given SKI -func (s *EEBUSServiceImpl) PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail { +func (s *Service) PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail { return s.connectionsHub.PairingDetailForSki(ski) } // Starts browsing for any EEBUS mDNS entry -func (s *EEBUSServiceImpl) StartBrowseMdnsEntries() { +func (s *Service) StartBrowseMdnsEntries() { s.connectionsHub.StartBrowseMdnsSearch() } // Stop brwosing for any EEBUS mDNS entry -func (s *EEBUSServiceImpl) StopBrowseMdnsEntries() { +func (s *Service) StopBrowseMdnsEntries() { s.connectionsHub.StopBrowseMdnsSearch() } // Sets a custom logging implementation // By default NoLogging is used, so no logs are printed -func (s *EEBUSServiceImpl) SetLogging(logger logging.Logging) { +func (s *Service) SetLogging(logger logging.LoggingInterface) { if logger == nil { return } @@ -126,7 +126,7 @@ func (s *EEBUSServiceImpl) SetLogging(logger logging.Logging) { } // Starts the service by initializeing mDNS and the server. -func (s *EEBUSServiceImpl) Setup() error { +func (s *Service) Setup() error { sd := s.configuration if len(sd.Certificate().Certificate) == 0 { @@ -172,7 +172,7 @@ func (s *EEBUSServiceImpl) Setup() error { deviceAdress := fmt.Sprintf("d:_i:%s_%s%s", vendor, sd.DeviceModel(), serial) // Create the local SPINE device - s.spineLocalDevice = spine.NewDeviceLocalImpl( + s.spineLocalDevice = spine.NewDeviceLocal( sd.DeviceBrand(), sd.DeviceModel(), sd.DeviceSerialNumber(), @@ -187,7 +187,7 @@ func (s *EEBUSServiceImpl) Setup() error { for _, entityType := range sd.EntityTypes() { entityAddressId := model.AddressEntityType(len(s.spineLocalDevice.Entities())) entityAddress := []model.AddressEntityType{entityAddressId} - entity := spine.NewEntityLocalImpl(s.spineLocalDevice, entityType, entityAddress) + entity := spine.NewEntityLocal(s.spineLocalDevice, entityType, entityAddress) s.spineLocalDevice.AddEntity(entity) } @@ -203,52 +203,52 @@ func (s *EEBUSServiceImpl) Setup() error { } // Starts the service -func (s *EEBUSServiceImpl) Start() { +func (s *Service) Start() { s.startOnce.Do(func() { s.connectionsHub.Start() }) } // Shutdown all services and stop the server. -func (s *EEBUSServiceImpl) Shutdown() { +func (s *Service) Shutdown() { // Shut down all running connections s.connectionsHub.Shutdown() } -func (s *EEBUSServiceImpl) Configuration() *api.Configuration { +func (s *Service) Configuration() *api.Configuration { return s.configuration } -func (s *EEBUSServiceImpl) LocalService() *shipapi.ServiceDetails { +func (s *Service) LocalService() *shipapi.ServiceDetails { return s.localService } -func (s *EEBUSServiceImpl) LocalDevice() spineapi.DeviceLocal { +func (s *Service) LocalDevice() spineapi.DeviceLocalInterface { return s.spineLocalDevice } // Returns the Service detail of a given remote SKI -func (s *EEBUSServiceImpl) RemoteServiceForSKI(ski string) *shipapi.ServiceDetails { +func (s *Service) RemoteServiceForSKI(ski string) *shipapi.ServiceDetails { return s.connectionsHub.ServiceForSKI(ski) } // Sets the SKI as being paired or not // and connect it if paired and not currently being connected -func (s *EEBUSServiceImpl) RegisterRemoteSKI(ski string, enable bool) { +func (s *Service) RegisterRemoteSKI(ski string, enable bool) { s.connectionsHub.RegisterRemoteSKI(ski, enable) } // Triggers the pairing process for a SKI -func (s *EEBUSServiceImpl) InitiatePairingWithSKI(ski string) { +func (s *Service) InitiatePairingWithSKI(ski string) { s.connectionsHub.InitiatePairingWithSKI(ski) } // Cancels the pairing process for a SKI -func (s *EEBUSServiceImpl) CancelPairingWithSKI(ski string) { +func (s *Service) CancelPairingWithSKI(ski string) { s.connectionsHub.CancelPairingWithSKI(ski) } // Close a connection to a remote SKI -func (s *EEBUSServiceImpl) DisconnectSKI(ski string, reason string) { +func (s *Service) DisconnectSKI(ski string, reason string) { s.connectionsHub.DisconnectSKI(ski, reason) } diff --git a/service/service_test.go b/service/service_test.go index 7d430a99..28869eba 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -26,26 +26,26 @@ type ServiceSuite struct { config *api.Configuration - sut *EEBUSServiceImpl + sut *Service - serviceHandler *mocks.EEBUSServiceHandler - conHub *shipmocks.Hub - logging *shipmocks.Logging + serviceReader *mocks.ServiceReaderInterface + conHub *shipmocks.HubInterface + logging *shipmocks.LoggingInterface } func (s *ServiceSuite) BeforeTest(suiteName, testName string) { - s.serviceHandler = mocks.NewEEBUSServiceHandler(s.T()) + s.serviceReader = mocks.NewServiceReaderInterface(s.T()) - s.conHub = shipmocks.NewHub(s.T()) + s.conHub = shipmocks.NewHubInterface(s.T()) - s.logging = shipmocks.NewLogging(s.T()) + s.logging = shipmocks.NewLoggingInterface(s.T()) certificate := tls.Certificate{} s.config, _ = api.NewConfiguration( "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, []model.EntityTypeType{model.EntityTypeTypeCEM}, 4729, certificate, 230.0, time.Second*4) - s.sut = NewEEBUSService(s.config, s.serviceHandler) + s.sut = NewService(s.config, s.serviceReader) } func (s *ServiceSuite) Test_EEBUSHandler() { @@ -56,23 +56,23 @@ func (s *ServiceSuite) Test_EEBUSHandler() { } entries := []*shipapi.MdnsEntry{entry} - s.serviceHandler.EXPECT().VisibleRemoteServicesUpdated(mock.Anything, mock.Anything).Return() + s.serviceReader.EXPECT().VisibleRemoteServicesUpdated(mock.Anything, mock.Anything).Return() s.sut.VisibleMDNSRecordsUpdated(entries) - s.serviceHandler.EXPECT().RemoteSKIConnected(mock.Anything, mock.Anything).Return() + s.serviceReader.EXPECT().RemoteSKIConnected(mock.Anything, mock.Anything).Return() s.sut.RemoteSKIConnected(testSki) - s.serviceHandler.EXPECT().RemoteSKIDisconnected(mock.Anything, mock.Anything).Return() + s.serviceReader.EXPECT().RemoteSKIDisconnected(mock.Anything, mock.Anything).Return() s.sut.RemoteSKIDisconnected(testSki) - s.serviceHandler.EXPECT().ServiceShipIDUpdate(mock.Anything, mock.Anything).Return() + s.serviceReader.EXPECT().ServiceShipIDUpdate(mock.Anything, mock.Anything).Return() s.sut.ServiceShipIDUpdate(testSki, "shipid") - s.serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return() + s.serviceReader.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return() detail := &shipapi.ConnectionStateDetail{} s.sut.ServicePairingDetailUpdate(testSki, detail) - s.serviceHandler.EXPECT().AllowWaitingForTrust(mock.Anything).Return(true) + s.serviceReader.EXPECT().AllowWaitingForTrust(mock.Anything).Return(true) result := s.sut.AllowWaitingForTrust(testSki) assert.Equal(s.T(), true, result) From 4d469455d4c601e49f189b362e55fa1e645b87e8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 25 Jan 2024 17:04:59 +0100 Subject: [PATCH 175/240] Update to latest spine --- features/feature.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/feature.go b/features/feature.go index b31658a9..18db7279 100644 --- a/features/feature.go +++ b/features/feature.go @@ -88,7 +88,7 @@ func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, el return nil, ErrOperationOnFunctionNotSupported } - msgCounter, fErr := f.featureLocal.RequestData(function, selectors, elements, f.featureRemote) + msgCounter, fErr := f.featureLocal.RequestRemoteData(function, selectors, elements, f.featureRemote) if fErr != nil { return nil, errors.New(fErr.String()) } diff --git a/go.mod b/go.mod index 12e31d97..c83a2da2 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 - github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f + github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index bb0447b5..fde7aeb4 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 h1:I54U0+MHwtsHUS7OLEt0z5fGNZOpV7G3EgQxRvFXeKI= github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f h1:BmUN9NM/mWblJIvvctkPgHN65/+ZmwyN1IF3a6Jzdy0= -github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f/go.mod h1:VkPWyFUVlf385o7NeesvAiYoDDDnYyLuIYYdhzfJ0pY= +github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb h1:H9mjA/l2lxZChvrruZLSew2Dafs6Yp6GAhHq/3lYfPc= +github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb/go.mod h1:VkPWyFUVlf385o7NeesvAiYoDDDnYyLuIYYdhzfJ0pY= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From db5d41b1400666c933a382c8efc0aa904d42afdd Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 25 Jan 2024 17:05:46 +0100 Subject: [PATCH 176/240] Update devicediagnosis feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename SendState to SetLocalState, to make it clearer what it does - Don’t trigger a notify, as setting the data will automatically do it --- features/devicediagnosis.go | 4 +--- features/devicediagnosis_test.go | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index 37a43666..a5a911b8 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -45,8 +45,6 @@ func (d *DeviceDiagnosis) GetState() (*model.DeviceDiagnosisStateDataType, error return data, nil } -func (d *DeviceDiagnosis) SendState(operatingState *model.DeviceDiagnosisStateDataType) { +func (d *DeviceDiagnosis) SetLocalState(operatingState *model.DeviceDiagnosisStateDataType) { d.featureLocal.SetData(model.FunctionTypeDeviceDiagnosisStateData, operatingState) - - _, _ = d.featureLocal.NotifyData(model.FunctionTypeDeviceDiagnosisStateData, nil, nil, false, nil, d.featureRemote) } diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 16f9fc2f..b7b5b97a 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -75,11 +75,11 @@ func (s *DeviceDiagnosisSuite) Test_GetState() { assert.NotNil(s.T(), result) } -func (s *DeviceDiagnosisSuite) Test_SendState() { +func (s *DeviceDiagnosisSuite) Test_SetState() { data := &model.DeviceDiagnosisStateDataType{ OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), PowerSupplyCondition: util.Ptr(model.PowerSupplyConditionTypeGood), } - s.deviceDiagnosis.SendState(data) + s.deviceDiagnosis.SetLocalState(data) assert.NotNil(s.T(), s.sentMessage) } From 53cd428894fc96c9e6c398e31a32f683ec2710a2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 16:26:54 +0100 Subject: [PATCH 177/240] More api tests --- api/configuration_test.go | 59 ++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/api/configuration_test.go b/api/configuration_test.go index 9ce8d13c..21389505 100644 --- a/api/configuration_test.go +++ b/api/configuration_test.go @@ -1,6 +1,7 @@ package api import ( + "crypto/tls" "testing" "time" @@ -26,45 +27,53 @@ func (s *ConfigurationSuite) Test_Configuration() { serial := "serial" port := 4567 volt := 230.0 + heartbeatTimeout := time.Second * 4 + entityTypes := []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM} config, err := NewConfiguration("", brand, model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, - []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, volt, time.Second*4) + entityTypes, 0, certificate, volt, heartbeatTimeout) + + assert.Nil(s.T(), config) + assert.NotNil(s.T(), err) + + config, err = NewConfiguration("", brand, model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, + entityTypes, port, certificate, volt, heartbeatTimeout) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) config, err = NewConfiguration(vendor, "", model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, - []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + entityTypes, port, certificate, 230, heartbeatTimeout) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) config, err = NewConfiguration(vendor, brand, "", serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, - []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + entityTypes, port, certificate, 230, heartbeatTimeout) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) config, err = NewConfiguration(vendor, brand, model, "", spinemodel.DeviceTypeTypeEnergyManagementSystem, - []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + entityTypes, port, certificate, 230, heartbeatTimeout) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) config, err = NewConfiguration(vendor, brand, model, serial, "", - []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + entityTypes, port, certificate, 230, heartbeatTimeout) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) config, err = NewConfiguration(vendor, brand, model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, - []spinemodel.EntityTypeType{}, port, certificate, 230, time.Second*4) + []spinemodel.EntityTypeType{}, port, certificate, 230, heartbeatTimeout) assert.Nil(s.T(), config) assert.NotNil(s.T(), err) config, err = NewConfiguration(vendor, brand, model, serial, spinemodel.DeviceTypeTypeEnergyManagementSystem, - []spinemodel.EntityTypeType{spinemodel.EntityTypeTypeCEM}, port, certificate, 230, time.Second*4) + entityTypes, port, certificate, 230, heartbeatTimeout) assert.NotNil(s.T(), config) assert.Nil(s.T(), err) @@ -73,8 +82,13 @@ func (s *ConfigurationSuite) Test_Configuration() { config.SetInterfaces(ifaces) assert.Equal(s.T(), 2, len(config.interfaces)) + ifacesValue := config.Interfaces() + assert.Equal(s.T(), ifaces, ifacesValue) + config.SetRegisterAutoAccept(true) assert.Equal(s.T(), true, config.registerAutoAccept) + registerValue := config.RegisterAutoAccept() + assert.Equal(s.T(), true, registerValue) id := config.generateIdentifier() assert.NotEqual(s.T(), "", id) @@ -97,4 +111,35 @@ func (s *ConfigurationSuite) Test_Configuration() { voltage := config.Voltage() assert.Equal(s.T(), volt, voltage) + + portValue := config.Port() + assert.Equal(s.T(), port, portValue) + + heartbeatValue := config.HeartbeatTimeout() + assert.Equal(s.T(), heartbeatTimeout, heartbeatValue) + + vendorValue := config.VendorCode() + assert.Equal(s.T(), vendor, vendorValue) + + deviceValue := config.DeviceBrand() + assert.Equal(s.T(), brand, deviceValue) + + modelValue := config.DeviceModel() + assert.Equal(s.T(), model, modelValue) + + serialValue := config.DeviceSerialNumber() + assert.Equal(s.T(), serial, serialValue) + + deviceTypeValue := config.DeviceType() + assert.Equal(s.T(), spinemodel.DeviceTypeTypeEnergyManagementSystem, deviceTypeValue) + + entityValues := config.EntityTypes() + assert.Equal(s.T(), entityTypes, entityValues) + featuresetValue := config.FeatureSet() + assert.Equal(s.T(), spinemodel.NetworkManagementFeatureSetTypeSmart, featuresetValue) + + testCert := tls.Certificate{} + config.SetCertificate(testCert) + certValue := config.Certificate() + assert.Equal(s.T(), testCert, certValue) } From 88d7959e205463c09287ee1ce7e5d31b1feea2a3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 16:44:14 +0100 Subject: [PATCH 178/240] More deviceconfiguration tests --- features/deviceconfiguration.go | 13 ++--- features/deviceconfiguration_test.go | 84 +++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index 717d842a..15945604 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -76,10 +76,9 @@ func (d *DeviceConfiguration) GetDescriptionForKeyName(keyName model.DeviceConfi } for _, item := range descriptions { - if item.KeyId == nil || item.KeyName == nil { - continue - } - if *item.KeyName == keyName { + if item.KeyId != nil && + item.KeyName != nil && + *item.KeyName == keyName { return &item, nil } } @@ -119,11 +118,7 @@ func (d *DeviceConfiguration) GetKeyValueForKeyName(keyname model.DeviceConfigur continue } - if desc.KeyName == nil { - continue - } - - if *desc.KeyName == keyname { + if desc.KeyName != nil && *desc.KeyName == keyname { switch valueType { case model.DeviceConfigurationKeyValueTypeTypeBoolean: return item.Value.Boolean, nil diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index 4534e99d..12dcb8af 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -2,13 +2,15 @@ package features_test import ( "testing" + "time" "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/util" - shipapi "github.com/enbility/ship-go/api" + shipmocks "github.com/enbility/ship-go/mocks" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -23,19 +25,15 @@ type DeviceConfigurationSuite struct { remoteEntity spineapi.EntityRemoteInterface deviceConfiguration *features.DeviceConfiguration - sentMessage []byte -} - -var _ shipapi.ShipConnectionDataWriterInterface = (*DeviceConfigurationSuite)(nil) - -func (s *DeviceConfigurationSuite) WriteShipMessageWithPayload(message []byte) { - s.sentMessage = message } func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) { + mockWriter := shipmocks.NewShipConnectionDataWriterInterface(s.T()) + mockWriter.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + s.localEntity, s.remoteEntity = setupFeatures( s.T(), - s, + mockWriter, []featureFunctions{ { featureType: model.FeatureTypeTypeDeviceConfiguration, @@ -116,6 +114,30 @@ func (s *DeviceConfigurationSuite) Test_GetValueForKey() { value, err = s.deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor, model.DeviceConfigurationKeyValueTypeTypeScaledNumber) assert.Nil(s.T(), err) assert.NotNil(s.T(), value) + + value, err = s.deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAzimuth, model.DeviceConfigurationKeyValueTypeTypeDate) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + + value, err = s.deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeBatteryType, model.DeviceConfigurationKeyValueTypeTypeDateTime) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + + value, err = s.deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeTimeToAcDischargePowerMax, model.DeviceConfigurationKeyValueTypeTypeDuration) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + + value, err = s.deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeIncentivesWaitIncentiveWriteable, model.DeviceConfigurationKeyValueTypeTypeTime) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + + value, err = s.deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeIncentivesWaitIncentiveWriteable, model.DeviceConfigurationKeyValueTypeType("invalid")) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), value) + + value, err = s.deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameType("invalid"), model.DeviceConfigurationKeyValueTypeType("invalid")) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), value) } func (s *DeviceConfigurationSuite) Test_GetValues() { @@ -158,6 +180,26 @@ func (s *DeviceConfigurationSuite) addDescription() { ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), Unit: util.Ptr(model.UnitOfMeasurementTypepct), }, + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(3)), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeAzimuth), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeDate), + }, + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(4)), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeBatteryType), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeDateTime), + }, + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(5)), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeTimeToAcDischargePowerMax), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), + }, + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(6)), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeIncentivesWaitIncentiveWriteable), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeTime), + }, }, } rF.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, fData, nil, nil) @@ -185,6 +227,30 @@ func (s *DeviceConfigurationSuite) addData() { ScaledNumber: model.NewScaledNumberType(50), }, }, + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(3)), + Value: &model.DeviceConfigurationKeyValueValueType{ + Date: model.NewDateType("01.01.2023"), + }, + }, + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(4)), + Value: &model.DeviceConfigurationKeyValueValueType{ + DateTime: model.NewDateTimeTypeFromTime(time.Now()), + }, + }, + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(5)), + Value: &model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(time.Second * 4), + }, + }, + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(6)), + Value: &model.DeviceConfigurationKeyValueValueType{ + Time: model.NewTimeType("13:05"), + }, + }, }, } rF.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, fData, nil, nil) From e290baa63891a9223cd8bfe09b56b7e88b383d3e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 16:47:12 +0100 Subject: [PATCH 179/240] Update ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c83a2da2..e146208b 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 - github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb + github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e + github.com/enbility/spine-go v0.0.0-20240128154609-2ff85b4d3fea github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index fde7aeb4..aa23f2d5 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 h1:I54U0+MHwtsHUS7OLEt0z5fGNZOpV7G3EgQxRvFXeKI= -github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb h1:H9mjA/l2lxZChvrruZLSew2Dafs6Yp6GAhHq/3lYfPc= -github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb/go.mod h1:VkPWyFUVlf385o7NeesvAiYoDDDnYyLuIYYdhzfJ0pY= +github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e h1:SEwRSLtyI33v4CjyqhfGTV2U+4EI9s8Z5ap5PJBGhbc= +github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240128154609-2ff85b4d3fea h1:UDIbZfZqVccgyW5nCSqWlvPh2sVt0IrzCcR9Ix7j/fA= +github.com/enbility/spine-go v0.0.0-20240128154609-2ff85b4d3fea/go.mod h1:ETVDSeiTPNJZ8WBAuYBIP+8+9qbDl8lpbKbiAisYMTg= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From 292d87df435d6a96483fc48f473e00c4e6d51a87 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 16:51:31 +0100 Subject: [PATCH 180/240] Updates to changed SPINE api --- api/api.go | 2 - features/deviceclassification_test.go | 2 +- features/deviceconfiguration_test.go | 4 +- features/devicediagnosis_test.go | 2 +- features/electricalconnection_test.go | 10 ++--- features/feature.go | 4 +- features/identification_test.go | 2 +- features/incentivetable.go | 4 +- features/incentivetable_test.go | 6 +-- features/loadcontrol.go | 2 +- features/loadcontrol_test.go | 4 +- features/measurement_test.go | 6 +-- features/timeseries.go | 2 +- features/timeseries_test.go | 6 +-- go.mod | 3 ++ mocks/ServiceInterface.go | 64 --------------------------- service/service.go | 10 ----- service/service_test.go | 6 --- 18 files changed, 30 insertions(+), 109 deletions(-) diff --git a/api/api.go b/api/api.go index 0c7c253e..086ea7d0 100644 --- a/api/api.go +++ b/api/api.go @@ -28,8 +28,6 @@ type ServiceInterface interface { // Passthough functions to ConnectionsHub PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail - StartBrowseMdnsEntries() - StopBrowseMdnsEntries() } // interface for receiving data for specific events from Service diff --git a/features/deviceclassification_test.go b/features/deviceclassification_test.go index f9180161..535764b3 100644 --- a/features/deviceclassification_test.go +++ b/features/deviceclassification_test.go @@ -63,7 +63,7 @@ func (s *DeviceClassificationSuite) Test_GetManufacturerDetails() { assert.NotNil(s.T(), err) assert.Nil(s.T(), result) - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.DeviceClassificationManufacturerDataType{ DeviceName: util.Ptr(model.DeviceClassificationStringType("brand")), DeviceCode: util.Ptr(model.DeviceClassificationStringType("brand")), diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index 12dcb8af..3b4c3804 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -161,7 +161,7 @@ func (s *DeviceConfigurationSuite) Test_GetValues() { // helper func (s *DeviceConfigurationSuite) addDescription() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ { @@ -206,7 +206,7 @@ func (s *DeviceConfigurationSuite) addDescription() { } func (s *DeviceConfigurationSuite) addData() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.DeviceConfigurationKeyValueListDataType{ DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ { diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index b7b5b97a..29bd2358 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -63,7 +63,7 @@ func (s *DeviceDiagnosisSuite) Test_GetState() { assert.NotNil(s.T(), err) assert.Nil(s.T(), result) - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.DeviceDiagnosisStateDataType{ OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), PowerSupplyCondition: util.Ptr(model.PowerSupplyConditionTypeGood), diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index e58c0617..7dffc949 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -292,7 +292,7 @@ func (s *ElectricalConnectionSuite) Test_AdjustValueToBeWithinPermittedValuesFor // helper func (s *ElectricalConnectionSuite) addDescription() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.ElectricalConnectionDescriptionListDataType{ ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ { @@ -307,7 +307,7 @@ func (s *ElectricalConnectionSuite) addDescription() { } func (s *ElectricalConnectionSuite) addParamDescriptionCurrents() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ { @@ -386,7 +386,7 @@ func (s *ElectricalConnectionSuite) addParamDescriptionCurrents() { } func (s *ElectricalConnectionSuite) addParamDescriptionPower() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ { @@ -401,7 +401,7 @@ func (s *ElectricalConnectionSuite) addParamDescriptionPower() { } func (s *ElectricalConnectionSuite) addPermittedValueSet() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.ElectricalConnectionPermittedValueSetListDataType{ ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ { @@ -478,7 +478,7 @@ func (s *ElectricalConnectionSuite) addPermittedValueSet() { } func (s *ElectricalConnectionSuite) addPermittedValueSetEmptyElli() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.ElectricalConnectionPermittedValueSetListDataType{ ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ { diff --git a/features/feature.go b/features/feature.go index 18db7279..d6cf4e4d 100644 --- a/features/feature.go +++ b/features/feature.go @@ -47,7 +47,7 @@ func (f *FeatureImpl) SubscribeForEntity() error { if f.featureRemote == nil { return errors.New("remote feature not available") } - if _, fErr := f.featureLocal.Subscribe(f.featureRemote.Address()); fErr != nil { + if _, fErr := f.featureLocal.SubscribeToRemote(f.featureRemote.Address()); fErr != nil { return errors.New(fErr.String()) } @@ -63,7 +63,7 @@ func (f *FeatureImpl) Bind() error { if f.featureRemote == nil { return errors.New("remote feature not available") } - if _, fErr := f.featureLocal.Bind(f.featureRemote.Address()); fErr != nil { + if _, fErr := f.featureLocal.BindToRemote(f.featureRemote.Address()); fErr != nil { return errors.New(fErr.String()) } diff --git a/features/identification_test.go b/features/identification_test.go index 85b236d2..ab029e9a 100644 --- a/features/identification_test.go +++ b/features/identification_test.go @@ -71,7 +71,7 @@ func (s *IdentificationSuite) Test_GetValues() { } func (s *IdentificationSuite) addData() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.IdentificationListDataType{ IdentificationData: []model.IdentificationDataType{ { diff --git a/features/incentivetable.go b/features/incentivetable.go index 3f21b5ef..46fda74f 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -55,7 +55,7 @@ func (i *IncentiveTable) WriteValues(data []model.IncentiveTableType) (*model.Ms }, } - return i.featureRemote.Sender().Write(i.featureLocal.Address(), i.featureRemote.Address(), cmd) + return i.remoteDevice.Sender().Write(i.featureLocal.Address(), i.featureRemote.Address(), cmd) } // return current values for Time Series @@ -86,7 +86,7 @@ func (i *IncentiveTable) WriteDescriptions(data []model.IncentiveTableDescriptio }, } - return i.featureRemote.Sender().Write(i.featureLocal.Address(), i.featureRemote.Address(), cmd) + return i.remoteDevice.Sender().Write(i.featureLocal.Address(), i.featureRemote.Address(), cmd) } // return list of descriptions diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 41ab9315..37f3b86e 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -218,7 +218,7 @@ func (s *IncentiveTableSuite) Test_GetConstraints() { // helpers func (s *IncentiveTableSuite) addData() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.IncentiveTableDataType{ IncentiveTable: []model.IncentiveTableType{ @@ -238,7 +238,7 @@ func (s *IncentiveTableSuite) addData() { } func (s *IncentiveTableSuite) addDescription() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.IncentiveTableDescriptionDataType{ IncentiveTableDescription: []model.IncentiveTableDescriptionType{ { @@ -277,7 +277,7 @@ func (s *IncentiveTableSuite) addDescription() { } func (s *IncentiveTableSuite) addConstraints() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.IncentiveTableConstraintsDataType{ IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ { diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 06201495..693cb255 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -117,7 +117,7 @@ func (l *LoadControl) WriteLimitValues(data []model.LoadControlLimitDataType) (* }, } - return l.featureRemote.Sender().Write(l.featureLocal.Address(), l.featureRemote.Address(), cmd) + return l.remoteDevice.Sender().Write(l.featureLocal.Address(), l.featureRemote.Address(), cmd) } // return limit data diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index 28e29245..fb954b14 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -182,7 +182,7 @@ func (s *LoadControlSuite) Test_GetLimitDataForLimitId() { // helper func (s *LoadControlSuite) addDescription() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.LoadControlLimitDescriptionListDataType{ LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ { @@ -196,7 +196,7 @@ func (s *LoadControlSuite) addDescription() { } func (s *LoadControlSuite) addData() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{ { diff --git a/features/measurement_test.go b/features/measurement_test.go index b336cd79..ce5c5483 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -153,7 +153,7 @@ func (s *MeasurementSuite) Test_GetValues() { // helper func (s *MeasurementSuite) addDescription() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ { @@ -175,7 +175,7 @@ func (s *MeasurementSuite) addDescription() { } func (s *MeasurementSuite) addConstraints() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.MeasurementConstraintsListDataType{ MeasurementConstraintsData: []model.MeasurementConstraintsDataType{ { @@ -196,7 +196,7 @@ func (s *MeasurementSuite) addConstraints() { } func (s *MeasurementSuite) addData() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) t := time.Now() fData := &model.MeasurementListDataType{ diff --git a/features/timeseries.go b/features/timeseries.go index 468d07b8..39619556 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -55,7 +55,7 @@ func (t *TimeSeries) WriteValues(data []model.TimeSeriesDataType) (*model.MsgCou }, } - return t.featureRemote.Sender().Write(t.featureLocal.Address(), t.featureRemote.Address(), cmd) + return t.remoteDevice.Sender().Write(t.featureLocal.Address(), t.featureRemote.Address(), cmd) } // return current values for Time Series diff --git a/features/timeseries_test.go b/features/timeseries_test.go index ea7f9e33..e6424549 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -182,7 +182,7 @@ func (s *TimeSeriesSuite) Test_GetConstraints() { // helpers func (s *TimeSeriesSuite) addData() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.TimeSeriesListDataType{ TimeSeriesData: []model.TimeSeriesDataType{ @@ -212,7 +212,7 @@ func (s *TimeSeriesSuite) addData() { } func (s *TimeSeriesSuite) addDescription() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.TimeSeriesDescriptionListDataType{ TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ { @@ -229,7 +229,7 @@ func (s *TimeSeriesSuite) addDescription() { } func (s *TimeSeriesSuite) addConstraints() { - rF := s.remoteEntity.Feature(util.Ptr(model.AddressFeatureType(1))) + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.TimeSeriesConstraintsListDataType{ TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ { diff --git a/go.mod b/go.mod index e146208b..090de68a 100644 --- a/go.mod +++ b/go.mod @@ -34,3 +34,6 @@ retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. ) + +// replace github.com/enbility/ship-go => ../ship-go +// replace github.com/enbility/spine-go => ../spine-go diff --git a/mocks/ServiceInterface.go b/mocks/ServiceInterface.go index 2456b842..2cb00d39 100644 --- a/mocks/ServiceInterface.go +++ b/mocks/ServiceInterface.go @@ -539,70 +539,6 @@ func (_c *ServiceInterface_Start_Call) RunAndReturn(run func()) *ServiceInterfac return _c } -// StartBrowseMdnsEntries provides a mock function with given fields: -func (_m *ServiceInterface) StartBrowseMdnsEntries() { - _m.Called() -} - -// ServiceInterface_StartBrowseMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartBrowseMdnsEntries' -type ServiceInterface_StartBrowseMdnsEntries_Call struct { - *mock.Call -} - -// StartBrowseMdnsEntries is a helper method to define mock.On call -func (_e *ServiceInterface_Expecter) StartBrowseMdnsEntries() *ServiceInterface_StartBrowseMdnsEntries_Call { - return &ServiceInterface_StartBrowseMdnsEntries_Call{Call: _e.mock.On("StartBrowseMdnsEntries")} -} - -func (_c *ServiceInterface_StartBrowseMdnsEntries_Call) Run(run func()) *ServiceInterface_StartBrowseMdnsEntries_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *ServiceInterface_StartBrowseMdnsEntries_Call) Return() *ServiceInterface_StartBrowseMdnsEntries_Call { - _c.Call.Return() - return _c -} - -func (_c *ServiceInterface_StartBrowseMdnsEntries_Call) RunAndReturn(run func()) *ServiceInterface_StartBrowseMdnsEntries_Call { - _c.Call.Return(run) - return _c -} - -// StopBrowseMdnsEntries provides a mock function with given fields: -func (_m *ServiceInterface) StopBrowseMdnsEntries() { - _m.Called() -} - -// ServiceInterface_StopBrowseMdnsEntries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StopBrowseMdnsEntries' -type ServiceInterface_StopBrowseMdnsEntries_Call struct { - *mock.Call -} - -// StopBrowseMdnsEntries is a helper method to define mock.On call -func (_e *ServiceInterface_Expecter) StopBrowseMdnsEntries() *ServiceInterface_StopBrowseMdnsEntries_Call { - return &ServiceInterface_StopBrowseMdnsEntries_Call{Call: _e.mock.On("StopBrowseMdnsEntries")} -} - -func (_c *ServiceInterface_StopBrowseMdnsEntries_Call) Run(run func()) *ServiceInterface_StopBrowseMdnsEntries_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *ServiceInterface_StopBrowseMdnsEntries_Call) Return() *ServiceInterface_StopBrowseMdnsEntries_Call { - _c.Call.Return() - return _c -} - -func (_c *ServiceInterface_StopBrowseMdnsEntries_Call) RunAndReturn(run func()) *ServiceInterface_StopBrowseMdnsEntries_Call { - _c.Call.Return(run) - return _c -} - // NewServiceInterface creates a new instance of ServiceInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewServiceInterface(t interface { diff --git a/service/service.go b/service/service.go index 3aec1add..6db18b50 100644 --- a/service/service.go +++ b/service/service.go @@ -106,16 +106,6 @@ func (s *Service) PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail return s.connectionsHub.PairingDetailForSki(ski) } -// Starts browsing for any EEBUS mDNS entry -func (s *Service) StartBrowseMdnsEntries() { - s.connectionsHub.StartBrowseMdnsSearch() -} - -// Stop brwosing for any EEBUS mDNS entry -func (s *Service) StopBrowseMdnsEntries() { - s.connectionsHub.StopBrowseMdnsSearch() -} - // Sets a custom logging implementation // By default NoLogging is used, so no logs are printed func (s *Service) SetLogging(logger logging.LoggingInterface) { diff --git a/service/service_test.go b/service/service_test.go index 28869eba..e6074a43 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -86,12 +86,6 @@ func (s *ServiceSuite) Test_ConnectionsHub() { s.conHub.EXPECT().PairingDetailForSki(mock.Anything).Return(nil) s.sut.PairingDetailForSki(testSki) - s.conHub.EXPECT().StartBrowseMdnsSearch().Return() - s.sut.StartBrowseMdnsEntries() - - s.conHub.EXPECT().StopBrowseMdnsSearch().Return() - s.sut.StopBrowseMdnsEntries() - s.conHub.EXPECT().ServiceForSKI(mock.Anything).Return(nil) details := s.sut.RemoteServiceForSKI(testSki) assert.Nil(s.T(), details) From a8243dc45b2dbb5011ab01619038fd3a17409a54 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 17:28:20 +0100 Subject: [PATCH 181/240] Update spine --- go.mod | 5 +---- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 090de68a..8f9a4fbd 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e - github.com/enbility/spine-go v0.0.0-20240128154609-2ff85b4d3fea + github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1 github.com/stretchr/testify v1.8.4 ) @@ -34,6 +34,3 @@ retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. ) - -// replace github.com/enbility/ship-go => ../ship-go -// replace github.com/enbility/spine-go => ../spine-go diff --git a/go.sum b/go.sum index aa23f2d5..6be60c14 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e h1:SEwRSLtyI33v4CjyqhfGTV2U+4EI9s8Z5ap5PJBGhbc= github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240128154609-2ff85b4d3fea h1:UDIbZfZqVccgyW5nCSqWlvPh2sVt0IrzCcR9Ix7j/fA= -github.com/enbility/spine-go v0.0.0-20240128154609-2ff85b4d3fea/go.mod h1:ETVDSeiTPNJZ8WBAuYBIP+8+9qbDl8lpbKbiAisYMTg= +github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1 h1:0S8EZeDfCzN0VZUyiiHOTgkTcnheN7Vuzfy5fG4ZDLU= +github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1/go.mod h1:ETVDSeiTPNJZ8WBAuYBIP+8+9qbDl8lpbKbiAisYMTg= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From 158bda90a8004baa61173d7696882907a0ab999e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 17:28:51 +0100 Subject: [PATCH 182/240] Update features tests --- features/api.go | 3 ++- features/deviceclassification_test.go | 2 +- features/deviceconfiguration_test.go | 2 +- features/devicediagnosis_test.go | 2 +- features/electricalconnection_test.go | 2 +- features/feature.go | 14 +++++++------- features/helper_test.go | 10 +++++++--- features/identification_test.go | 2 +- features/incentivetable_test.go | 2 +- features/loadcontrol_test.go | 12 +++++++++++- features/measurement_test.go | 2 +- features/timeseries_test.go | 2 +- 12 files changed, 35 insertions(+), 20 deletions(-) diff --git a/features/api.go b/features/api.go index 53797d71..b96d50d3 100644 --- a/features/api.go +++ b/features/api.go @@ -6,6 +6,7 @@ import ( ) type Feature interface { - SubscribeForEntity() error + Subscribe() error + Bind() error AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) } diff --git a/features/deviceclassification_test.go b/features/deviceclassification_test.go index 535764b3..fdc3c083 100644 --- a/features/deviceclassification_test.go +++ b/features/deviceclassification_test.go @@ -47,7 +47,7 @@ func (s *DeviceClassificationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceClassification, err = features.NewDeviceClassification(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.deviceClassification, err = features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceClassification) } diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index 3b4c3804..fe745c47 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -46,7 +46,7 @@ func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceConfiguration, err = features.NewDeviceConfiguration(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.deviceConfiguration, err = features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceConfiguration) } diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 29bd2358..bb0601ae 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -47,7 +47,7 @@ func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceDiagnosis, err = features.NewDeviceDiagnosis(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.deviceDiagnosis, err = features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceDiagnosis) } diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 7dffc949..a18f69d5 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -49,7 +49,7 @@ func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) { ) var err error - s.electricalConnection, err = features.NewElectricalConnection(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.electricalConnection, err = features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.electricalConnection) } diff --git a/features/feature.go b/features/feature.go index d6cf4e4d..a47429b1 100644 --- a/features/feature.go +++ b/features/feature.go @@ -42,8 +42,8 @@ func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole mod return f, err } -// subscribe to the feature for a the entity -func (f *FeatureImpl) SubscribeForEntity() error { +// subscribe to the feature of the entity +func (f *FeatureImpl) Subscribe() error { if f.featureRemote == nil { return errors.New("remote feature not available") } @@ -54,11 +54,7 @@ func (f *FeatureImpl) SubscribeForEntity() error { return nil } -func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) { - f.featureLocal.AddResultCallback(msgCounterReference, function) -} - -// bind to the feature of a the entity +// bind to the feature of the entity func (f *FeatureImpl) Bind() error { if f.featureRemote == nil { return errors.New("remote feature not available") @@ -70,6 +66,10 @@ func (f *FeatureImpl) Bind() error { return nil } +func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) { + f.featureLocal.AddResultCallback(msgCounterReference, function) +} + // helper method which adds checking if the feature is available and the operation is allowed // selectors and elements are used if specific data should be requested by using // model.FilterType DataSelectors (selectors) and/or DataElements (elements) diff --git a/features/helper_test.go b/features/helper_test.go index d21549d4..357403e0 100644 --- a/features/helper_test.go +++ b/features/helper_test.go @@ -103,13 +103,14 @@ func setupFeatures( localDevice := spine.NewDeviceLocal("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode", "TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4) localEntity := spine.NewEntityLocal(localDevice, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1})) - localDevice.AddEntity(localEntity) for i, item := range featureFunctions { - f := spine.NewFeatureLocal(uint(i+1), localEntity, item.featureType, model.RoleTypeServer) + f := spine.NewFeatureLocal(uint(i+1), localEntity, item.featureType, model.RoleTypeClient) localEntity.AddFeature(f) } + localDevice.AddEntity(localEntity) + remoteDeviceName := "remoteDevice" sender := spine.NewSender(dataCon) remoteDevice := spine.NewDeviceRemote(localDevice, "test", sender) @@ -144,7 +145,7 @@ func setupFeatures( Feature: util.Ptr(model.AddressFeatureType(i + 1)), }, FeatureType: util.Ptr(item.featureType), - Role: util.Ptr(model.RoleTypeClient), + Role: util.Ptr(model.RoleTypeServer), }, } var supportedFcts []model.FunctionPropertyType @@ -168,6 +169,9 @@ func setupFeatures( assert.Nil(t, err) assert.NotNil(t, remoteEntities) assert.NotEqual(t, 0, len(remoteEntities)) + remoteDevice.UpdateDevice(data.DeviceInformation.Description) + + localDevice.AddRemoteDeviceForSki("test", remoteDevice) return localEntity, remoteEntities[0] } diff --git a/features/identification_test.go b/features/identification_test.go index ab029e9a..4f842bc8 100644 --- a/features/identification_test.go +++ b/features/identification_test.go @@ -47,7 +47,7 @@ func (s *IdentificationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.identification, err = features.NewIdentification(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.identification, err = features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.identification) } diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 37f3b86e..6f1b1bc4 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -49,7 +49,7 @@ func (s *IncentiveTableSuite) BeforeTest(suiteName, testName string) { ) var err error - s.incentiveTable, err = features.NewIncentiveTable(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.incentiveTable, err = features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.incentiveTable) } diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index fb954b14..e3abfaff 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -49,11 +49,21 @@ func (s *LoadControlSuite) BeforeTest(suiteName, testName string) { ) var err error - s.loadControl, err = features.NewLoadControl(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.loadControl, err = features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.loadControl) } +func (s *LoadControlSuite) Test_Bind() { + err := s.loadControl.Bind() + assert.Nil(s.T(), err) +} + +func (s *LoadControlSuite) Test_Subscribe() { + err := s.loadControl.Subscribe() + assert.Nil(s.T(), err) +} + func (s *LoadControlSuite) Test_RequestLimitDescription() { err := s.loadControl.RequestLimitDescriptions() assert.Nil(s.T(), err) diff --git a/features/measurement_test.go b/features/measurement_test.go index ce5c5483..804a069e 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -58,7 +58,7 @@ func (s *MeasurementSuite) BeforeTest(suiteName, testName string) { ) var err error - s.measurement, err = features.NewMeasurement(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.measurement, err = features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.measurement) } diff --git a/features/timeseries_test.go b/features/timeseries_test.go index e6424549..e625dc3c 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -50,7 +50,7 @@ func (s *TimeSeriesSuite) BeforeTest(suiteName, testName string) { ) var err error - s.timeSeries, err = features.NewTimeSeries(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity) + s.timeSeries, err = features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.timeSeries) } From e78cbfd10ba17466b573eba621e210074c485333 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 17:28:59 +0100 Subject: [PATCH 183/240] Update github action --- .github/workflows/default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index a4e514ca..9d0dccf8 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -50,7 +50,7 @@ jobs: # we let the report trigger content trigger a failure using the GitHub Security features. args: '-no-fail -fmt sarif -out results.sarif ./...' - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: # Path to SARIF file relative to the root of the repository sarif_file: results.sarif \ No newline at end of file From 7d8a49b116eb6e427c2204c339f07eff4cd0b240 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 21:12:26 +0100 Subject: [PATCH 184/240] Update Github actions --- .github/workflows/default.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 9d0dccf8..ead54325 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -29,7 +29,7 @@ jobs: run: go build -v ./... - name: Lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@master with: version: latest skip-pkg-cache: true @@ -40,9 +40,9 @@ jobs: run: go test -race -v -coverprofile=coverage.out -covermode=atomic ./... - name: Send coverage - uses: shogo82148/actions-goveralls@v1 + uses: coverallsapp/github-action@v2 with: - path-to-profile: coverage.out + file: coverage.out - name: Run Gosec Security Scanner uses: securego/gosec@master From 02552d237ad9cda5cf97910d58d1a8211a95e232 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 21:12:36 +0100 Subject: [PATCH 185/240] Update ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8f9a4fbd..2ef3316d 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e - github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1 + github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d + github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 6be60c14..38fd9a79 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e h1:SEwRSLtyI33v4CjyqhfGTV2U+4EI9s8Z5ap5PJBGhbc= -github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1 h1:0S8EZeDfCzN0VZUyiiHOTgkTcnheN7Vuzfy5fG4ZDLU= -github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1/go.mod h1:ETVDSeiTPNJZ8WBAuYBIP+8+9qbDl8lpbKbiAisYMTg= +github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d h1:zxstkLtwd0K1+ayEj1jeXciMyU6vOkE3hIZiSfVRWfA= +github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c h1:bpJPdXlRB0NzfwcIHq/N64YG48r7syEnsYTAF8kEogw= +github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c/go.mod h1:QkF2SPaU8djEJ8qd5FeraOUI+x2gwONnS4QrUxl8I3s= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From 995d96f93a29f557b44501a8a7e3827e32ae66ec Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 30 Jan 2024 22:18:59 +0100 Subject: [PATCH 186/240] Add RequestHeartbeat for DeviceDiagnosis --- features/devicediagnosis.go | 5 +++++ features/devicediagnosis_test.go | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index a5a911b8..bf6ca69b 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -48,3 +48,8 @@ func (d *DeviceDiagnosis) GetState() (*model.DeviceDiagnosisStateDataType, error func (d *DeviceDiagnosis) SetLocalState(operatingState *model.DeviceDiagnosisStateDataType) { d.featureLocal.SetData(model.FunctionTypeDeviceDiagnosisStateData, operatingState) } + +// request FunctionTypeDeviceDiagnosisHeartbeatData from a remote device +func (d *DeviceDiagnosis) RequestHeartbeat() (*model.MsgCounterType, error) { + return d.requestData(model.FunctionTypeDeviceDiagnosisHeartbeatData, nil, nil) +} diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index bb0601ae..0656f9fd 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -52,6 +52,12 @@ func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) { assert.NotNil(s.T(), s.deviceDiagnosis) } +func (s *DeviceDiagnosisSuite) Test_RequestHeartbeat() { + counter, err := s.deviceDiagnosis.RequestHeartbeat() + assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) +} + func (s *DeviceDiagnosisSuite) Test_RequestState() { counter, err := s.deviceDiagnosis.RequestState() assert.Nil(s.T(), err) From d6ffe0f47b208d972d9571f17a6dd81fb27463f3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 30 Jan 2024 22:33:15 +0100 Subject: [PATCH 187/240] Fix test --- features/devicediagnosis_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 0656f9fd..7c598fad 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -41,6 +41,7 @@ func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) { featureType: model.FeatureTypeTypeDeviceDiagnosis, functions: []model.FunctionType{ model.FunctionTypeDeviceDiagnosisStateData, + model.FunctionTypeDeviceDiagnosisHeartbeatData, }, }, }, From c6c34ba5618b6863ab557355256031894e0a9e45 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 31 Jan 2024 19:45:20 +0100 Subject: [PATCH 188/240] Update ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2ef3316d..4abf1193 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d - github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c + github.com/enbility/ship-go v0.0.0-20240129091849-749ac33e69f9 + github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 38fd9a79..22af539f 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d h1:zxstkLtwd0K1+ayEj1jeXciMyU6vOkE3hIZiSfVRWfA= -github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c h1:bpJPdXlRB0NzfwcIHq/N64YG48r7syEnsYTAF8kEogw= -github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c/go.mod h1:QkF2SPaU8djEJ8qd5FeraOUI+x2gwONnS4QrUxl8I3s= +github.com/enbility/ship-go v0.0.0-20240129091849-749ac33e69f9 h1:7E/2jFIY/koOFj5JDf4nmLgho8nJB9njE2sg9SbEDCw= +github.com/enbility/ship-go v0.0.0-20240129091849-749ac33e69f9/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3 h1:e+E/tSRxBY2bVfNe/joPM0JrUdifCh+CvBfPIS82bN4= +github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3/go.mod h1:QkF2SPaU8djEJ8qd5FeraOUI+x2gwONnS4QrUxl8I3s= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From e6ae6b9eac420fcbea0c2c1a47d8d75af75d55e1 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 31 Jan 2024 20:02:02 +0100 Subject: [PATCH 189/240] Add DeviceDiagnosis helper for heartbeat check --- features/devicediagnosis.go | 25 +++++++++++++++++++++++++ features/devicediagnosis_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index bf6ca69b..dc231d37 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -1,6 +1,8 @@ package features import ( + "time" + "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -53,3 +55,26 @@ func (d *DeviceDiagnosis) SetLocalState(operatingState *model.DeviceDiagnosisSta func (d *DeviceDiagnosis) RequestHeartbeat() (*model.MsgCounterType, error) { return d.requestData(model.FunctionTypeDeviceDiagnosisHeartbeatData, nil, nil) } + +// check if the currently available heartbeat data is within a time duration +func (d *DeviceDiagnosis) IsHeartbeatWithinDuration(duration time.Duration) bool { + rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceDiagnosisHeartbeatData) + if rData == nil { + return false + } + + data := rData.(*model.DeviceDiagnosisHeartbeatDataType) + if data == nil || data.Timestamp == nil { + return false + } + + timeValue, err := data.Timestamp.GetTime() + if err != nil { + return false + } + + now := time.Now() + diff := now.Sub(timeValue) + + return diff < duration +} diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 7c598fad..4bd43769 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -2,6 +2,7 @@ package features_test import ( "testing" + "time" "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/util" @@ -90,3 +91,33 @@ func (s *DeviceDiagnosisSuite) Test_SetState() { s.deviceDiagnosis.SetLocalState(data) assert.NotNil(s.T(), s.sentMessage) } + +func (s *DeviceDiagnosisSuite) Test_IsHeartbeatWithinDuration() { + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) + + result := s.deviceDiagnosis.IsHeartbeatWithinDuration(time.Second * 10) + assert.Equal(s.T(), false, result) + + now := time.Now() + + data := &model.DeviceDiagnosisHeartbeatDataType{ + HeartbeatCounter: util.Ptr(uint64(1)), + HeartbeatTimeout: model.NewDurationType(time.Second * 4), + } + + rF.UpdateData(model.FunctionTypeDeviceDiagnosisHeartbeatData, data, nil, nil) + + result = s.deviceDiagnosis.IsHeartbeatWithinDuration(time.Second * 10) + assert.Equal(s.T(), false, result) + + data.Timestamp = model.NewAbsoluteOrRelativeTimeTypeFromTime(now) + rF.UpdateData(model.FunctionTypeDeviceDiagnosisHeartbeatData, data, nil, nil) + + result = s.deviceDiagnosis.IsHeartbeatWithinDuration(time.Second * 10) + assert.Equal(s.T(), true, result) + + time.Sleep(time.Second * 2) + + result = s.deviceDiagnosis.IsHeartbeatWithinDuration(time.Second * 1) + assert.Equal(s.T(), false, result) +} From 71dba36d05b052c249554351e9fd4a268127c166 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 8 Feb 2024 21:43:17 +0100 Subject: [PATCH 190/240] Update ship --- go.mod | 4 +++- go.sum | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 4abf1193..14adfb6e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240129091849-749ac33e69f9 + github.com/enbility/ship-go v0.0.0-20240208203308-81a899a2fdcd github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3 github.com/stretchr/testify v1.8.4 ) @@ -34,3 +34,5 @@ retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. ) + +replace github.com/holoplot/go-avahi => github.com/DerAndereAndi/go-avahi v0.0.0-20240123155759-b4b6b2b50b4d diff --git a/go.sum b/go.sum index 22af539f..0a242a96 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/DerAndereAndi/go-avahi v0.0.0-20240123155759-b4b6b2b50b4d h1:CAPfJuSmxb0RamS3TZu1eOaz9KfD8IT0nPBLzYKvdFg= +github.com/DerAndereAndi/go-avahi v0.0.0-20240123155759-b4b6b2b50b4d/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df h1:qYVips8l0s1ldVoLSup0m+hYh5cMMA4ndcvocxhZuMc= github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df/go.mod h1:OO5/UahoVBLyauLdDF4httPlSISqbrWDbHi9k99zUsc= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= @@ -5,8 +7,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240129091849-749ac33e69f9 h1:7E/2jFIY/koOFj5JDf4nmLgho8nJB9njE2sg9SbEDCw= -github.com/enbility/ship-go v0.0.0-20240129091849-749ac33e69f9/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/ship-go v0.0.0-20240208203308-81a899a2fdcd h1:UExumZ9bYpqWNkyIZxkFI/mhRcbAZaupc30Z7cbAw+U= +github.com/enbility/ship-go v0.0.0-20240208203308-81a899a2fdcd/go.mod h1:icguYD3/26zyAECxff8WaJ/wlW8o6mBJW6+B2uU8/2s= github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3 h1:e+E/tSRxBY2bVfNe/joPM0JrUdifCh+CvBfPIS82bN4= github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3/go.mod h1:QkF2SPaU8djEJ8qd5FeraOUI+x2gwONnS4QrUxl8I3s= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -17,8 +19,6 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed h1:AMm8KKtfeEhUlj45DYJBSMW2VcLO1Tss3jaMUqb+VvE= -github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= From fca7be1d31230d9fd87bffd42c3037d326e8fe49 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 8 Feb 2024 21:43:41 +0100 Subject: [PATCH 191/240] Add option to specify mDNS providers --- api/configuration.go | 21 +++++++++++++++++---- api/configuration_test.go | 6 ++++++ service/service.go | 12 ++++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/api/configuration.go b/api/configuration.go index 250a3129..69b5280e 100644 --- a/api/configuration.go +++ b/api/configuration.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/enbility/ship-go/mdns" "github.com/enbility/spine-go/model" ) @@ -79,6 +80,9 @@ type Configuration struct { // The timeout to be used for sending heartbeats heartbeatTimeout time.Duration + + // Optional set which mDNS providers should be used + mdnsProviderSelection mdns.MdnsProviderSelection } // Setup a Configuration with the required parameters @@ -95,10 +99,11 @@ func NewConfiguration( heartbeatTimeout time.Duration, ) (*Configuration, error) { configuration := &Configuration{ - certificate: certificate, - port: port, - voltage: voltage, - heartbeatTimeout: heartbeatTimeout, + certificate: certificate, + port: port, + voltage: voltage, + heartbeatTimeout: heartbeatTimeout, + mdnsProviderSelection: mdns.MdnsProviderSelectionAll, } if port == 0 { @@ -172,6 +177,14 @@ func (s *Configuration) SetAlternateMdnsServiceName(name string) { s.alternateMdnsServiceName = name } +func (s *Configuration) SetMdnsProviderSelection(providerSelection mdns.MdnsProviderSelection) { + s.mdnsProviderSelection = providerSelection +} + +func (s *Configuration) MdnsProviderSelection() mdns.MdnsProviderSelection { + return s.mdnsProviderSelection +} + func (s *Configuration) DeviceType() model.DeviceTypeType { return s.deviceType } diff --git a/api/configuration_test.go b/api/configuration_test.go index 21389505..bce02d58 100644 --- a/api/configuration_test.go +++ b/api/configuration_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/enbility/ship-go/cert" + "github.com/enbility/ship-go/mdns" spinemodel "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -78,6 +79,11 @@ func (s *ConfigurationSuite) Test_Configuration() { assert.NotNil(s.T(), config) assert.Nil(s.T(), err) + assert.Equal(s.T(), mdns.MdnsProviderSelectionAll, config.MdnsProviderSelection()) + + config.SetMdnsProviderSelection(mdns.MdnsProviderSelectionAvahiOnly) + assert.Equal(s.T(), mdns.MdnsProviderSelectionAvahiOnly, config.MdnsProviderSelection()) + ifaces := []string{"lo", "eth0"} config.SetInterfaces(ifaces) assert.Equal(s.T(), 2, len(config.interfaces)) diff --git a/service/service.go b/service/service.go index 6db18b50..083de400 100644 --- a/service/service.go +++ b/service/service.go @@ -183,8 +183,16 @@ func (s *Service) Setup() error { // setup mDNS mdns := mdns.NewMDNS( - s.localService.SKI(), sd.DeviceBrand(), sd.DeviceModel(), string(sd.DeviceType()), - sd.Identifier(), sd.MdnsServiceName(), sd.Port(), sd.Interfaces()) + s.localService.SKI(), + sd.DeviceBrand(), + sd.DeviceModel(), + string(sd.DeviceType()), + sd.Identifier(), + sd.MdnsServiceName(), + sd.Port(), + sd.Interfaces(), + sd.MdnsProviderSelection(), + ) // Setup connections hub with mDNS and websocket connection handling s.connectionsHub = hub.NewHub(s, mdns, s.configuration.Port(), s.configuration.Certificate(), s.localService) From 53e41447262cf0117edfdfd1a51f2298a358d167 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 10 Feb 2024 11:32:02 +0100 Subject: [PATCH 192/240] Update ship, spine and other dependencies --- go.mod | 18 ++++++------- go.sum | 79 ++++++++++++---------------------------------------------- 2 files changed, 24 insertions(+), 73 deletions(-) diff --git a/go.mod b/go.mod index 14adfb6e..ce337b92 100644 --- a/go.mod +++ b/go.mod @@ -3,29 +3,29 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240208203308-81a899a2fdcd - github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3 + github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 + github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a github.com/stretchr/testify v1.8.4 ) require ( - github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df // indirect github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed // indirect - github.com/miekg/dns v1.1.57 // indirect + github.com/holoplot/go-avahi v0.0.0-20240210093433-b8dc0fc11e7e // indirect + github.com/miekg/dns v1.1.58 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/date v1.20.5 // indirect github.com/rickb777/plural v1.4.1 // indirect github.com/stretchr/objx v0.5.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -34,5 +34,3 @@ retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. ) - -replace github.com/holoplot/go-avahi => github.com/DerAndereAndi/go-avahi v0.0.0-20240123155759-b4b6b2b50b4d diff --git a/go.sum b/go.sum index 0a242a96..05f81de7 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,14 @@ -github.com/DerAndereAndi/go-avahi v0.0.0-20240123155759-b4b6b2b50b4d h1:CAPfJuSmxb0RamS3TZu1eOaz9KfD8IT0nPBLzYKvdFg= -github.com/DerAndereAndi/go-avahi v0.0.0-20240123155759-b4b6b2b50b4d/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= -github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df h1:qYVips8l0s1ldVoLSup0m+hYh5cMMA4ndcvocxhZuMc= -github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df/go.mod h1:OO5/UahoVBLyauLdDF4httPlSISqbrWDbHi9k99zUsc= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240208203308-81a899a2fdcd h1:UExumZ9bYpqWNkyIZxkFI/mhRcbAZaupc30Z7cbAw+U= -github.com/enbility/ship-go v0.0.0-20240208203308-81a899a2fdcd/go.mod h1:icguYD3/26zyAECxff8WaJ/wlW8o6mBJW6+B2uU8/2s= -github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3 h1:e+E/tSRxBY2bVfNe/joPM0JrUdifCh+CvBfPIS82bN4= -github.com/enbility/spine-go v0.0.0-20240131184223-dc84aefee7a3/go.mod h1:QkF2SPaU8djEJ8qd5FeraOUI+x2gwONnS4QrUxl8I3s= +github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 h1:Ufq7ObSy69KSBv5hBouVcuk6h2IuLM6WDm3AgM9cN9I= +github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a h1:2pQeQmBfxA7AgmBGtEkPMWsdH5+E74VTvRpuFgc5cu8= +github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= +github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= +github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -19,9 +17,10 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= -github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= -github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/holoplot/go-avahi v0.0.0-20240210093433-b8dc0fc11e7e h1:XOKmPp6CgtFByseoBaL5Ew9b6NWSie+nr6pMFeO0Tvc= +github.com/holoplot/go-avahi v0.0.0-20240210093433-b8dc0fc11e7e/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -40,68 +39,22 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From c18c0ddc28606a4e3a3b9eb5993147502373d41e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 15 Feb 2024 19:17:37 +0100 Subject: [PATCH 193/240] Update spine and adopt new spine helper --- features/deviceclassification.go | 10 +++------- features/deviceconfiguration.go | 19 +++++------------- features/devicediagnosis.go | 19 +++++------------- features/electricalconnection.go | 26 +++++++----------------- features/identification.go | 10 +++------- features/incentivetable.go | 28 +++++++------------------- features/loadcontrol.go | 19 +++++------------- features/measurement.go | 29 ++++++++------------------- features/timeseries.go | 34 +++++++------------------------- go.mod | 2 +- go.sum | 4 ++-- 11 files changed, 53 insertions(+), 147 deletions(-) diff --git a/features/deviceclassification.go b/features/deviceclassification.go index 17c6cdfd..e1615d2c 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -3,6 +3,7 @@ package features import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type DeviceClassification struct { @@ -32,13 +33,8 @@ func (d *DeviceClassification) RequestManufacturerDetails() (*model.MsgCounterTy // get the current manufacturer details for a remote device entity func (d *DeviceClassification) GetManufacturerDetails() (*model.DeviceClassificationManufacturerDataType, error) { - rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceClassificationManufacturerData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.DeviceClassificationManufacturerDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceClassificationManufacturerDataType](d.featureRemote, model.FunctionTypeDeviceClassificationManufacturerData) + if err != nil { return nil, ErrDataNotAvailable } diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index 15945604..e3b58ab7 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -3,6 +3,7 @@ package features import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type DeviceConfiguration struct { @@ -38,13 +39,8 @@ func (d *DeviceConfiguration) RequestKeyValues() (*model.MsgCounterType, error) // return current descriptions for Device Configuration func (d *DeviceConfiguration) GetDescriptions() ([]model.DeviceConfigurationKeyValueDescriptionDataType, error) { - rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.DeviceConfigurationKeyValueDescriptionListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType](d.featureRemote, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData) + if err != nil { return nil, ErrDataNotAvailable } @@ -88,13 +84,8 @@ func (d *DeviceConfiguration) GetDescriptionForKeyName(keyName model.DeviceConfi // return current values for Device Configuration func (d *DeviceConfiguration) GetKeyValues() ([]model.DeviceConfigurationKeyValueDataType, error) { - rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceConfigurationKeyValueListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.DeviceConfigurationKeyValueListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueListDataType](d.featureRemote, model.FunctionTypeDeviceConfigurationKeyValueListData) + if err != nil { return nil, ErrDataNotAvailable } diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index dc231d37..537c3a74 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -5,6 +5,7 @@ import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type DeviceDiagnosis struct { @@ -34,13 +35,8 @@ func (d *DeviceDiagnosis) RequestState() (*model.MsgCounterType, error) { // get the current diagnosis state for an device entity func (d *DeviceDiagnosis) GetState() (*model.DeviceDiagnosisStateDataType, error) { - rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceDiagnosisStateData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.DeviceDiagnosisStateDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceDiagnosisStateDataType](d.featureRemote, model.FunctionTypeDeviceDiagnosisStateData) + if err != nil { return nil, ErrDataNotAvailable } @@ -58,13 +54,8 @@ func (d *DeviceDiagnosis) RequestHeartbeat() (*model.MsgCounterType, error) { // check if the currently available heartbeat data is within a time duration func (d *DeviceDiagnosis) IsHeartbeatWithinDuration(duration time.Duration) bool { - rData := d.featureRemote.DataCopy(model.FunctionTypeDeviceDiagnosisHeartbeatData) - if rData == nil { - return false - } - - data := rData.(*model.DeviceDiagnosisHeartbeatDataType) - if data == nil || data.Timestamp == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceDiagnosisHeartbeatDataType](d.featureRemote, model.FunctionTypeDeviceDiagnosisHeartbeatData) + if err != nil || data == nil || data.Timestamp == nil { return false } diff --git a/features/electricalconnection.go b/features/electricalconnection.go index b8fb63a8..a465d04a 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -3,6 +3,7 @@ package features import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type ElectricalConnection struct { @@ -46,12 +47,8 @@ func (e *ElectricalConnection) RequestPermittedValueSets() (*model.MsgCounterTyp // return list of description for Electrical Connection func (e *ElectricalConnection) GetDescriptions() ([]model.ElectricalConnectionDescriptionDataType, error) { - rData := e.featureRemote.DataCopy(model.FunctionTypeElectricalConnectionDescriptionListData) - if rData == nil { - return nil, ErrMetadataNotAvailable - } - data := rData.(*model.ElectricalConnectionDescriptionListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionDescriptionListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionDescriptionListData) + if err != nil { return nil, ErrMetadataNotAvailable } @@ -85,12 +82,8 @@ func (e *ElectricalConnection) GetDescriptionForMeasurementId(measurementId mode // return parameter descriptions for all Electrical Connections func (e *ElectricalConnection) GetParameterDescriptions() ([]model.ElectricalConnectionParameterDescriptionDataType, error) { - rData := e.featureRemote.DataCopy(model.FunctionTypeElectricalConnectionParameterDescriptionListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - data := rData.(*model.ElectricalConnectionParameterDescriptionListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionParameterDescriptionListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionParameterDescriptionListData) + if err != nil { return nil, ErrDataNotAvailable } @@ -171,13 +164,8 @@ func (e *ElectricalConnection) GetParameterDescriptionForMeasuredPhase(phase mod // return permitted values for all Electrical Connections func (e *ElectricalConnection) GetPermittedValueSets() ([]model.ElectricalConnectionPermittedValueSetDataType, error) { - rData := e.featureRemote.DataCopy(model.FunctionTypeElectricalConnectionPermittedValueSetListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.ElectricalConnectionPermittedValueSetListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionPermittedValueSetListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionPermittedValueSetListData) + if err != nil { return nil, ErrDataNotAvailable } diff --git a/features/identification.go b/features/identification.go index c065c002..3c1051f7 100644 --- a/features/identification.go +++ b/features/identification.go @@ -3,6 +3,7 @@ package features import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type Identification struct { @@ -32,13 +33,8 @@ func (i *Identification) RequestValues() (*model.MsgCounterType, error) { // return current values for Identification func (i *Identification) GetValues() ([]model.IdentificationDataType, error) { - rData := i.featureRemote.DataCopy(model.FunctionTypeIdentificationListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.IdentificationListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.IdentificationListDataType](i.featureRemote, model.FunctionTypeIdentificationListData) + if err != nil { return nil, ErrDataNotAvailable } diff --git a/features/incentivetable.go b/features/incentivetable.go index 46fda74f..13b8a6e1 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -3,6 +3,7 @@ package features import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type IncentiveTable struct { @@ -60,13 +61,8 @@ func (i *IncentiveTable) WriteValues(data []model.IncentiveTableType) (*model.Ms // return current values for Time Series func (i *IncentiveTable) GetValues() ([]model.IncentiveTableType, error) { - rData := i.featureRemote.DataCopy(model.FunctionTypeIncentiveTableData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.IncentiveTableDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.IncentiveTableDataType](i.featureRemote, model.FunctionTypeIncentiveTableData) + if err != nil { return nil, ErrDataNotAvailable } @@ -91,13 +87,8 @@ func (i *IncentiveTable) WriteDescriptions(data []model.IncentiveTableDescriptio // return list of descriptions func (i *IncentiveTable) GetDescriptions() ([]model.IncentiveTableDescriptionType, error) { - rData := i.featureRemote.DataCopy(model.FunctionTypeIncentiveTableDescriptionData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.IncentiveTableDescriptionDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.IncentiveTableDescriptionDataType](i.featureRemote, model.FunctionTypeIncentiveTableDescriptionData) + if err != nil { return nil, ErrDataNotAvailable } @@ -127,13 +118,8 @@ func (i *IncentiveTable) GetDescriptionsForScope(scope model.ScopeTypeType) ([]m // return list of constraints func (i *IncentiveTable) GetConstraints() ([]model.IncentiveTableConstraintsType, error) { - rData := i.featureRemote.DataCopy(model.FunctionTypeIncentiveTableConstraintsData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.IncentiveTableConstraintsDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.IncentiveTableConstraintsDataType](i.featureRemote, model.FunctionTypeIncentiveTableConstraintsData) + if err != nil { return nil, ErrDataNotAvailable } diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 693cb255..81457e96 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -3,6 +3,7 @@ package features import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type LoadControl struct { @@ -45,16 +46,11 @@ func (l *LoadControl) RequestLimitValues() (*model.MsgCounterType, error) { // returns the load control limit descriptions // returns an error if no description data is available yet func (l *LoadControl) GetLimitDescriptions() ([]model.LoadControlLimitDescriptionDataType, error) { - rData := l.featureRemote.DataCopy(model.FunctionTypeLoadControlLimitDescriptionListData) - if rData == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType](l.featureRemote, model.FunctionTypeLoadControlLimitDescriptionListData) + if err != nil { return nil, ErrMetadataNotAvailable } - data := rData.(*model.LoadControlLimitDescriptionListDataType) - if data == nil { - return nil, ErrDataNotAvailable - } - return data.LoadControlLimitDescriptionData, nil } @@ -122,13 +118,8 @@ func (l *LoadControl) WriteLimitValues(data []model.LoadControlLimitDataType) (* // return limit data func (l *LoadControl) GetLimitValues() ([]model.LoadControlLimitDataType, error) { - rData := l.featureRemote.DataCopy(model.FunctionTypeLoadControlLimitListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.LoadControlLimitListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.LoadControlLimitListDataType](l.featureRemote, model.FunctionTypeLoadControlLimitListData) + if err != nil { return nil, ErrDataNotAvailable } diff --git a/features/measurement.go b/features/measurement.go index c5c2a288..e8659611 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -3,6 +3,7 @@ package features import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type Measurement struct { @@ -45,12 +46,8 @@ func (m *Measurement) RequestValues() (*model.MsgCounterType, error) { // return list of descriptions func (m *Measurement) GetDescriptions() ([]model.MeasurementDescriptionDataType, error) { - rData := m.featureRemote.DataCopy(model.FunctionTypeMeasurementDescriptionListData) - if rData == nil { - return nil, ErrMetadataNotAvailable - } - data := rData.(*model.MeasurementDescriptionListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.MeasurementDescriptionListDataType](m.featureRemote, model.FunctionTypeMeasurementDescriptionListData) + if err != nil { return nil, ErrMetadataNotAvailable } @@ -99,14 +96,9 @@ func (m *Measurement) GetDescriptionForMeasurementId(measurementId model.Measure // return current values for measurements func (m *Measurement) GetValues() ([]model.MeasurementDataType, error) { - rData := m.featureRemote.DataCopy(model.FunctionTypeMeasurementListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.MeasurementListDataType) - if data == nil { - return nil, ErrDataNotAvailable + data, err := spine.RemoteFeatureDataCopyOfType[*model.MeasurementListDataType](m.featureRemote, model.FunctionTypeMeasurementListData) + if err != nil { + return nil, ErrMetadataNotAvailable } return data.MeasurementData, nil @@ -147,15 +139,10 @@ func (m *Measurement) GetValuesForTypeCommodityScope(measurement model.Measureme // return measurement constraints func (m *Measurement) GetConstraints() ([]model.MeasurementConstraintsDataType, error) { - rData := m.featureRemote.DataCopy(model.FunctionTypeMeasurementConstraintsListData) - if rData == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.MeasurementConstraintsListDataType](m.featureRemote, model.FunctionTypeMeasurementConstraintsListData) + if err != nil { return nil, ErrMetadataNotAvailable } - data := rData.(*model.MeasurementConstraintsListDataType) - if data == nil { - return nil, ErrDataNotAvailable - } - return data.MeasurementConstraintsData, nil } diff --git a/features/timeseries.go b/features/timeseries.go index 39619556..dcd25130 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -3,6 +3,7 @@ package features import ( "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type TimeSeries struct { @@ -60,13 +61,8 @@ func (t *TimeSeries) WriteValues(data []model.TimeSeriesDataType) (*model.MsgCou // return current values for Time Series func (t *TimeSeries) GetValues() ([]model.TimeSeriesDataType, error) { - rData := t.featureRemote.DataCopy(model.FunctionTypeTimeSeriesListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.TimeSeriesListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.TimeSeriesListDataType](t.featureRemote, model.FunctionTypeTimeSeriesListData) + if err != nil { return nil, ErrDataNotAvailable } @@ -103,13 +99,8 @@ func (t *TimeSeries) GetValueForType(timeSeriesType model.TimeSeriesTypeType) (* // return list of descriptions func (t *TimeSeries) GetDescriptions() ([]model.TimeSeriesDescriptionDataType, error) { - rData := t.featureRemote.DataCopy(model.FunctionTypeTimeSeriesDescriptionListData) - if rData == nil { - return nil, ErrDataNotAvailable - } - - data := rData.(*model.TimeSeriesDescriptionListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.TimeSeriesDescriptionListDataType](t.featureRemote, model.FunctionTypeTimeSeriesDescriptionListData) + if err != nil { return nil, ErrDataNotAvailable } @@ -148,19 +139,8 @@ func (t *TimeSeries) GetDescriptionForType(timeSeriesType model.TimeSeriesTypeTy // return current constraints for Time Series func (t *TimeSeries) GetConstraints() ([]model.TimeSeriesConstraintsDataType, error) { - rData := t.featureRemote.DataCopy(model.FunctionTypeTimeSeriesConstraintsListData) - - // the codefactor warning is invalid, as .(type) check can not be replaced with if then - //revive:disable-next-line - switch constraintsData := rData.(type) { - case *model.TimeSeriesConstraintsListDataType: - if constraintsData == nil { - return nil, ErrDataNotAvailable - } - } - - data := rData.(*model.TimeSeriesConstraintsListDataType) - if data == nil { + data, err := spine.RemoteFeatureDataCopyOfType[*model.TimeSeriesConstraintsListDataType](t.featureRemote, model.FunctionTypeTimeSeriesConstraintsListData) + if err != nil { return nil, ErrDataNotAvailable } diff --git a/go.mod b/go.mod index ce337b92..1f6d90b3 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 - github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a + github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 05f81de7..6536cba6 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 h1:Ufq7ObSy69KSBv5hBouVcuk6h2IuLM6WDm3AgM9cN9I= github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a h1:2pQeQmBfxA7AgmBGtEkPMWsdH5+E74VTvRpuFgc5cu8= -github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= +github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f h1:VRQSmiN661D+p433V3ufgpg2vI58SJcCnXUbyA1vwn8= +github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From ffd98c4aabc0071ee4f02db7a057e940d5cadf14 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 15 Feb 2024 19:31:57 +0100 Subject: [PATCH 194/240] Update Github action --- .github/workflows/default.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index ead54325..6a41f08d 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -23,7 +23,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: ^1.18 + go-version: ^1.21 - name: Build run: go build -v ./... @@ -37,7 +37,10 @@ jobs: args: --timeout=3m --issues-exit-code=0 ./... - name: Test - run: go test -race -v -coverprofile=coverage.out -covermode=atomic ./... + run: go test -race -v -coverprofile=coverage_temp.out -covermode=atomic ./... + + - name: Remove mocks and cmd from coverage + run: grep -v -e "/eebus-go/mocks/" -e "/eebus-go/cmd/" coverage_temp.out > coverage.out - name: Send coverage uses: coverallsapp/github-action@v2 From ff26e3c2d34397e7ce2bcade785be51114509cbc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 18 Feb 2024 16:11:47 +0100 Subject: [PATCH 195/240] Various updates - Update ship - Add comments to interfaces - Adjust to ship interface changes - Separate hub related code into separate file --- api/api.go | 46 +++++++++++++--- go.mod | 2 +- go.sum | 4 +- mocks/ServiceInterface.go | 22 ++++---- mocks/ServiceReaderInterface.go | 2 +- service/service.go | 97 +++++++-------------------------- service/service_hub.go | 50 +++++++++++++++++ service/service_test.go | 23 ++++++-- 8 files changed, 143 insertions(+), 103 deletions(-) create mode 100644 service/service_hub.go diff --git a/api/api.go b/api/api.go index 086ea7d0..0cf17a4e 100644 --- a/api/api.go +++ b/api/api.go @@ -11,36 +11,68 @@ import ( /* Service */ +// central service interface +// +// implemented by service, used by the eebus service implementation type ServiceInterface interface { + // setup the service Setup() error + + // start the service Start() + + // shutdown the service Shutdown() + + // set logging interface SetLogging(logger logging.LoggingInterface) + // return the configuration Configuration() *Configuration + + // return the local service details LocalService() *shipapi.ServiceDetails + + // return the local device LocalDevice() spineapi.DeviceLocalInterface + + // Passthough functions to HubInterface + + // Provide the current pairing state for a SKI + PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail + + // Returns the Service detail of a given remote SKI RemoteServiceForSKI(ski string) *shipapi.ServiceDetails + + // Sets the SKI as being paired or not RegisterRemoteSKI(ski string, enable bool) - InitiatePairingWithSKI(ski string) - CancelPairingWithSKI(ski string) + + // Disconnect a connection to an SKI DisconnectSKI(ski string, reason string) - // Passthough functions to ConnectionsHub - PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail + // Triggers the pairing process for a SKI + InitiateOrApprovePairingWithSKI(ski string) + + // Cancels the pairing process for a SKI + CancelPairingWithSKI(ski string) } // interface for receiving data for specific events from Service +// +// some are passthrough readers, because service needs to coordinate +// everything with SPINE +// +// implemented by the eebus service implementation, used by service type ServiceReaderInterface interface { - // report all currently visible EEBUS services - VisibleRemoteServicesUpdated(service ServiceInterface, entries []shipapi.RemoteService) - // report a connection to a SKI RemoteSKIConnected(service ServiceInterface, ski string) // report a disconnection to a SKI RemoteSKIDisconnected(service ServiceInterface, ski string) + // report all currently visible EEBUS services + VisibleRemoteServicesUpdated(service ServiceInterface, entries []shipapi.RemoteService) + // Provides the SHIP ID the remote service reported during the handshake process // This needs to be persisted and passed on for future remote service connections // when using `PairRemoteService` diff --git a/go.mod b/go.mod index 1f6d90b3..05a0c323 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 + github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 6536cba6..76fc8325 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 h1:Ufq7ObSy69KSBv5hBouVcuk6h2IuLM6WDm3AgM9cN9I= -github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 h1:BmevZOzjfBjGFB4U8iYPgnY8zDhDJbAODksJ5tzLRfg= +github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f h1:VRQSmiN661D+p433V3ufgpg2vI58SJcCnXUbyA1vwn8= github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= diff --git a/mocks/ServiceInterface.go b/mocks/ServiceInterface.go index 2cb00d39..9d27634b 100644 --- a/mocks/ServiceInterface.go +++ b/mocks/ServiceInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.3. DO NOT EDIT. package mocks @@ -140,35 +140,35 @@ func (_c *ServiceInterface_DisconnectSKI_Call) RunAndReturn(run func(string, str return _c } -// InitiatePairingWithSKI provides a mock function with given fields: ski -func (_m *ServiceInterface) InitiatePairingWithSKI(ski string) { +// InitiateOrApprovePairingWithSKI provides a mock function with given fields: ski +func (_m *ServiceInterface) InitiateOrApprovePairingWithSKI(ski string) { _m.Called(ski) } -// ServiceInterface_InitiatePairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitiatePairingWithSKI' -type ServiceInterface_InitiatePairingWithSKI_Call struct { +// ServiceInterface_InitiateOrApprovePairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitiateOrApprovePairingWithSKI' +type ServiceInterface_InitiateOrApprovePairingWithSKI_Call struct { *mock.Call } -// InitiatePairingWithSKI is a helper method to define mock.On call +// InitiateOrApprovePairingWithSKI is a helper method to define mock.On call // - ski string -func (_e *ServiceInterface_Expecter) InitiatePairingWithSKI(ski interface{}) *ServiceInterface_InitiatePairingWithSKI_Call { - return &ServiceInterface_InitiatePairingWithSKI_Call{Call: _e.mock.On("InitiatePairingWithSKI", ski)} +func (_e *ServiceInterface_Expecter) InitiateOrApprovePairingWithSKI(ski interface{}) *ServiceInterface_InitiateOrApprovePairingWithSKI_Call { + return &ServiceInterface_InitiateOrApprovePairingWithSKI_Call{Call: _e.mock.On("InitiateOrApprovePairingWithSKI", ski)} } -func (_c *ServiceInterface_InitiatePairingWithSKI_Call) Run(run func(ski string)) *ServiceInterface_InitiatePairingWithSKI_Call { +func (_c *ServiceInterface_InitiateOrApprovePairingWithSKI_Call) Run(run func(ski string)) *ServiceInterface_InitiateOrApprovePairingWithSKI_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(string)) }) return _c } -func (_c *ServiceInterface_InitiatePairingWithSKI_Call) Return() *ServiceInterface_InitiatePairingWithSKI_Call { +func (_c *ServiceInterface_InitiateOrApprovePairingWithSKI_Call) Return() *ServiceInterface_InitiateOrApprovePairingWithSKI_Call { _c.Call.Return() return _c } -func (_c *ServiceInterface_InitiatePairingWithSKI_Call) RunAndReturn(run func(string)) *ServiceInterface_InitiatePairingWithSKI_Call { +func (_c *ServiceInterface_InitiateOrApprovePairingWithSKI_Call) RunAndReturn(run func(string)) *ServiceInterface_InitiateOrApprovePairingWithSKI_Call { _c.Call.Return(run) return _c } diff --git a/mocks/ServiceReaderInterface.go b/mocks/ServiceReaderInterface.go index 963f2a71..da8ee045 100644 --- a/mocks/ServiceReaderInterface.go +++ b/mocks/ServiceReaderInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.3. DO NOT EDIT. package mocks diff --git a/service/service.go b/service/service.go index 083de400..78d6fa51 100644 --- a/service/service.go +++ b/service/service.go @@ -44,77 +44,8 @@ func NewService(configuration *api.Configuration, serviceHandler api.ServiceRead } } -var _ shipapi.HubReaderInterface = (*Service)(nil) - -func (s *Service) VisibleMDNSRecordsUpdated(entries []*shipapi.MdnsEntry) { - var remoteServices []shipapi.RemoteService - - for _, entry := range entries { - remoteService := shipapi.RemoteService{ - Name: entry.Name, - Ski: entry.Ski, - Identifier: entry.Identifier, - Brand: entry.Brand, - Type: entry.Type, - Model: entry.Model, - } - - remoteServices = append(remoteServices, remoteService) - } - s.serviceHandler.VisibleRemoteServicesUpdated(s, remoteServices) -} - -// report a connection to a SKI -func (s *Service) RemoteSKIConnected(ski string) { - s.serviceHandler.RemoteSKIConnected(s, ski) -} - -// report a disconnection to a SKI -func (s *Service) RemoteSKIDisconnected(ski string) { - if s.spineLocalDevice != nil { - s.spineLocalDevice.RemoveRemoteDeviceConnection(ski) - } - - s.serviceHandler.RemoteSKIDisconnected(s, ski) -} - -// Provides the SHIP ID the remote service reported during the handshake process -func (s *Service) ServiceShipIDUpdate(ski string, shipdID string) { - s.serviceHandler.ServiceShipIDUpdate(ski, shipdID) -} - -// Provides the current pairing state for the remote service -// This is called whenever the state changes and can be used to -// provide user information for the pairing/connection process -func (s *Service) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { - s.serviceHandler.ServicePairingDetailUpdate(ski, detail) -} - -func (s *Service) SetupRemoteDevice(ski string, writeI shipapi.ShipConnectionDataWriterInterface) shipapi.ShipConnectionDataReaderInterface { - return s.LocalDevice().SetupRemoteDevice(ski, writeI) -} - -// return if the user is still able to trust the connection -func (s *Service) AllowWaitingForTrust(ski string) bool { - return s.serviceHandler.AllowWaitingForTrust(ski) -} - var _ api.ServiceInterface = (*Service)(nil) -// Get the current pairing details for a given SKI -func (s *Service) PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail { - return s.connectionsHub.PairingDetailForSki(ski) -} - -// Sets a custom logging implementation -// By default NoLogging is used, so no logs are printed -func (s *Service) SetLogging(logger logging.LoggingInterface) { - if logger == nil { - return - } - logging.SetLogging(logger) -} - // Starts the service by initializeing mDNS and the server. func (s *Service) Setup() error { sd := s.configuration @@ -225,6 +156,20 @@ func (s *Service) LocalDevice() spineapi.DeviceLocalInterface { return s.spineLocalDevice } +// Sets a custom logging implementation +// By default NoLogging is used, so no logs are printed +func (s *Service) SetLogging(logger logging.LoggingInterface) { + if logger == nil { + return + } + logging.SetLogging(logger) +} + +// Get the current pairing details for a given SKI +func (s *Service) PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail { + return s.connectionsHub.PairingDetailForSki(ski) +} + // Returns the Service detail of a given remote SKI func (s *Service) RemoteServiceForSKI(ski string) *shipapi.ServiceDetails { return s.connectionsHub.ServiceForSKI(ski) @@ -236,17 +181,17 @@ func (s *Service) RegisterRemoteSKI(ski string, enable bool) { s.connectionsHub.RegisterRemoteSKI(ski, enable) } +// Close a connection to a remote SKI +func (s *Service) DisconnectSKI(ski string, reason string) { + s.connectionsHub.DisconnectSKI(ski, reason) +} + // Triggers the pairing process for a SKI -func (s *Service) InitiatePairingWithSKI(ski string) { - s.connectionsHub.InitiatePairingWithSKI(ski) +func (s *Service) InitiateOrApprovePairingWithSKI(ski string) { + s.connectionsHub.InitiateOrApprovePairingWithSKI(ski) } // Cancels the pairing process for a SKI func (s *Service) CancelPairingWithSKI(ski string) { s.connectionsHub.CancelPairingWithSKI(ski) } - -// Close a connection to a remote SKI -func (s *Service) DisconnectSKI(ski string, reason string) { - s.connectionsHub.DisconnectSKI(ski, reason) -} diff --git a/service/service_hub.go b/service/service_hub.go new file mode 100644 index 00000000..cd285719 --- /dev/null +++ b/service/service_hub.go @@ -0,0 +1,50 @@ +package service + +import ( + shipapi "github.com/enbility/ship-go/api" +) + +var _ shipapi.HubReaderInterface = (*Service)(nil) + +// report a connection to a SKI +func (s *Service) RemoteSKIConnected(ski string) { + s.serviceHandler.RemoteSKIConnected(s, ski) +} + +// report a disconnection to a SKI +func (s *Service) RemoteSKIDisconnected(ski string) { + if s.spineLocalDevice != nil { + s.spineLocalDevice.RemoveRemoteDeviceConnection(ski) + } + + s.serviceHandler.RemoteSKIDisconnected(s, ski) +} + +// report an approved handshake by a remote device +func (s *Service) SetupRemoteDevice(ski string, writeI shipapi.ShipConnectionDataWriterInterface) shipapi.ShipConnectionDataReaderInterface { + return s.LocalDevice().SetupRemoteDevice(ski, writeI) +} + +// report all currently visible EEBUS services +func (s *Service) VisibleRemoteServicesUpdated(entries []shipapi.RemoteService) { + s.serviceHandler.VisibleRemoteServicesUpdated(s, entries) +} + +// Provides the SHIP ID the remote service reported during the handshake process +// This needs to be persisted and passed on for future remote service connections +// when using `PairRemoteService` +func (s *Service) ServiceShipIDUpdate(ski string, shipdID string) { + s.serviceHandler.ServiceShipIDUpdate(ski, shipdID) +} + +// Provides the current pairing state for the remote service +// This is called whenever the state changes and can be used to +// provide user information for the pairing/connection process +func (s *Service) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { + s.serviceHandler.ServicePairingDetailUpdate(ski, detail) +} + +// return if the user is still able to trust the connection +func (s *Service) AllowWaitingForTrust(ski string) bool { + return s.serviceHandler.AllowWaitingForTrust(ski) +} diff --git a/service/service_test.go b/service/service_test.go index e6074a43..18c7e084 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -11,6 +11,7 @@ import ( "github.com/enbility/ship-go/cert" "github.com/enbility/ship-go/logging" shipmocks "github.com/enbility/ship-go/mocks" + spinemocks "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -31,8 +32,11 @@ type ServiceSuite struct { serviceReader *mocks.ServiceReaderInterface conHub *shipmocks.HubInterface logging *shipmocks.LoggingInterface + localDevice *spinemocks.DeviceLocalInterface } +func (s *ServiceSuite) WriteShipMessageWithPayload(message []byte) {} + func (s *ServiceSuite) BeforeTest(suiteName, testName string) { s.serviceReader = mocks.NewServiceReaderInterface(s.T()) @@ -40,6 +44,8 @@ func (s *ServiceSuite) BeforeTest(suiteName, testName string) { s.logging = shipmocks.NewLoggingInterface(s.T()) + s.localDevice = spinemocks.NewDeviceLocalInterface(s.T()) + certificate := tls.Certificate{} s.config, _ = api.NewConfiguration( "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, @@ -51,18 +57,21 @@ func (s *ServiceSuite) BeforeTest(suiteName, testName string) { func (s *ServiceSuite) Test_EEBUSHandler() { testSki := "test" - entry := &shipapi.MdnsEntry{ + s.sut.spineLocalDevice = s.localDevice + + entry := shipapi.RemoteService{ Ski: testSki, } - entries := []*shipapi.MdnsEntry{entry} + entries := []shipapi.RemoteService{entry} s.serviceReader.EXPECT().VisibleRemoteServicesUpdated(mock.Anything, mock.Anything).Return() - s.sut.VisibleMDNSRecordsUpdated(entries) + s.sut.VisibleRemoteServicesUpdated(entries) s.serviceReader.EXPECT().RemoteSKIConnected(mock.Anything, mock.Anything).Return() s.sut.RemoteSKIConnected(testSki) s.serviceReader.EXPECT().RemoteSKIDisconnected(mock.Anything, mock.Anything).Return() + s.localDevice.EXPECT().RemoveRemoteDeviceConnection(testSki).Return() s.sut.RemoteSKIDisconnected(testSki) s.serviceReader.EXPECT().ServiceShipIDUpdate(mock.Anything, mock.Anything).Return() @@ -82,6 +91,7 @@ func (s *ServiceSuite) Test_ConnectionsHub() { testSki := "test" s.sut.connectionsHub = s.conHub + s.sut.spineLocalDevice = s.localDevice s.conHub.EXPECT().PairingDetailForSki(mock.Anything).Return(nil) s.sut.PairingDetailForSki(testSki) @@ -90,11 +100,14 @@ func (s *ServiceSuite) Test_ConnectionsHub() { details := s.sut.RemoteServiceForSKI(testSki) assert.Nil(s.T(), details) + s.localDevice.EXPECT().SetupRemoteDevice(mock.Anything, s).Return(nil) + s.sut.SetupRemoteDevice(testSki, s) + s.conHub.EXPECT().RegisterRemoteSKI(mock.Anything, mock.Anything).Return() s.sut.RegisterRemoteSKI(testSki, true) - s.conHub.EXPECT().InitiatePairingWithSKI(mock.Anything).Return() - s.sut.InitiatePairingWithSKI(testSki) + s.conHub.EXPECT().InitiateOrApprovePairingWithSKI(mock.Anything).Return() + s.sut.InitiateOrApprovePairingWithSKI(testSki) s.conHub.EXPECT().CancelPairingWithSKI(mock.Anything).Return() s.sut.CancelPairingWithSKI(testSki) From 0457306c3e3a0ecd2b256f11c065534a6c1800af Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 19 Feb 2024 20:29:01 +0100 Subject: [PATCH 196/240] Update spine and clean up feature api --- features/api.go | 17 +++++-- features/deviceclassification.go | 6 +-- features/deviceconfiguration.go | 11 +++-- features/deviceconfiguration_test.go | 3 +- features/devicediagnosis.go | 6 +-- features/electricalconnection.go | 18 +++----- features/electricalconnection_test.go | 6 ++- features/feature.go | 64 ++++++++++++++++----------- features/identification.go | 6 +-- features/incentivetable.go | 16 +++---- features/incentivetable_test.go | 6 ++- features/loadcontrol.go | 16 +++---- features/loadcontrol_test.go | 16 ++----- features/measurement.go | 6 +-- features/timeseries.go | 6 +-- go.mod | 5 ++- go.sum | 4 +- 17 files changed, 114 insertions(+), 98 deletions(-) diff --git a/features/api.go b/features/api.go index b96d50d3..1f281c7d 100644 --- a/features/api.go +++ b/features/api.go @@ -5,8 +5,19 @@ import ( "github.com/enbility/spine-go/model" ) -type Feature interface { - Subscribe() error - Bind() error +type FeatureInterface interface { + // check if there is a subscription to the remote feature + HasSubscription() bool + + // subscribe to the feature of the entity + Subscribe() (*model.MsgCounterType, error) + + // check if there is a binding to the remote feature + HasBinding() bool + + // bind to the feature of the entity + Bind() (*model.MsgCounterType, error) + + // add a callback function to be invoked once a result to a msgCounter came in AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) } diff --git a/features/deviceclassification.go b/features/deviceclassification.go index e1615d2c..82ce3190 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -7,20 +7,20 @@ import ( ) type DeviceClassification struct { - *FeatureImpl + *Feature } func NewDeviceClassification( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*DeviceClassification, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } dc := &DeviceClassification{ - FeatureImpl: feature, + Feature: feature, } return dc, nil diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index e3b58ab7..c5ae264e 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -7,29 +7,28 @@ import ( ) type DeviceConfiguration struct { - *FeatureImpl + *Feature } func NewDeviceConfiguration( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*DeviceConfiguration, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } dc := &DeviceConfiguration{ - FeatureImpl: feature, + Feature: feature, } return dc, nil } // request DeviceConfiguration data from a remote entity -func (d *DeviceConfiguration) RequestDescriptions() error { - _, err := d.requestData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, nil, nil) - return err +func (d *DeviceConfiguration) RequestDescriptions() (*model.MsgCounterType, error) { + return d.requestData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, nil, nil) } // request DeviceConfigurationKeyValueListDataType from a remote entity diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index fe745c47..2f009add 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -52,8 +52,9 @@ func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) { } func (s *DeviceConfigurationSuite) Test_RequestDescriptions() { - err := s.deviceConfiguration.RequestDescriptions() + counter, err := s.deviceConfiguration.RequestDescriptions() assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) } func (s *DeviceConfigurationSuite) Test_RequestKeyValueList() { diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index 537c3a74..2fb09eaa 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -9,20 +9,20 @@ import ( ) type DeviceDiagnosis struct { - *FeatureImpl + *Feature } func NewDeviceDiagnosis( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*DeviceDiagnosis, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } dd := &DeviceDiagnosis{ - FeatureImpl: feature, + Feature: feature, } return dd, nil diff --git a/features/electricalconnection.go b/features/electricalconnection.go index a465d04a..9b4e02e8 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -7,37 +7,33 @@ import ( ) type ElectricalConnection struct { - *FeatureImpl + *Feature } func NewElectricalConnection( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*ElectricalConnection, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } e := &ElectricalConnection{ - FeatureImpl: feature, + Feature: feature, } return e, nil } // request ElectricalConnectionDescriptionListDataType from a remote entity -func (e *ElectricalConnection) RequestDescriptions() error { - _, err := e.requestData(model.FunctionTypeElectricalConnectionDescriptionListData, nil, nil) - - return err +func (e *ElectricalConnection) RequestDescriptions() (*model.MsgCounterType, error) { + return e.requestData(model.FunctionTypeElectricalConnectionDescriptionListData, nil, nil) } // request FunctionTypeElectricalConnectionParameterDescriptionListData from a remote entity -func (e *ElectricalConnection) RequestParameterDescriptions() error { - _, err := e.requestData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, nil, nil) - - return err +func (e *ElectricalConnection) RequestParameterDescriptions() (*model.MsgCounterType, error) { + return e.requestData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, nil, nil) } // request FunctionTypeElectricalConnectionPermittedValueSetListData from a remote entity diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index a18f69d5..8dd09567 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -55,13 +55,15 @@ func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) { } func (s *ElectricalConnectionSuite) Test_RequestDescriptions() { - err := s.electricalConnection.RequestDescriptions() + counter, err := s.electricalConnection.RequestDescriptions() assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) } func (s *ElectricalConnectionSuite) Test_RequestParameterDescriptions() { - err := s.electricalConnection.RequestParameterDescriptions() + counter, err := s.electricalConnection.RequestParameterDescriptions() assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) } func (s *ElectricalConnectionSuite) Test_RequestPermittedValueSets() { diff --git a/features/feature.go b/features/feature.go index a47429b1..33ada565 100644 --- a/features/feature.go +++ b/features/feature.go @@ -7,7 +7,7 @@ import ( "github.com/enbility/spine-go/model" ) -type FeatureImpl struct { +type Feature struct { featureType model.FeatureTypeType localRole model.RoleType @@ -23,10 +23,18 @@ type FeatureImpl struct { remoteEntity api.EntityRemoteInterface } -var _ Feature = (*FeatureImpl)(nil) +var _ FeatureInterface = (*Feature)(nil) -func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*FeatureImpl, error) { - f := &FeatureImpl{ +func NewFeature(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*Feature, error) { + if localEntity == nil { + return nil, errors.New("local entity is nil") + } + + if remoteEntity == nil { + return nil, errors.New("remote entity is nil") + } + + f := &Feature{ featureType: featureType, localRole: localRole, remoteRole: remoteRole, @@ -42,31 +50,41 @@ func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole mod return f, err } +// check if there is a subscription to the remote feature +func (f *Feature) HasSubscription() bool { + subscription := f.featureLocal.HasSubscriptionToRemote(f.featureRemote.Address()) + return subscription +} + // subscribe to the feature of the entity -func (f *FeatureImpl) Subscribe() error { - if f.featureRemote == nil { - return errors.New("remote feature not available") - } - if _, fErr := f.featureLocal.SubscribeToRemote(f.featureRemote.Address()); fErr != nil { - return errors.New(fErr.String()) +func (f *Feature) Subscribe() (*model.MsgCounterType, error) { + msgCounter, fErr := f.featureLocal.SubscribeToRemote(f.featureRemote.Address()) + + if fErr != nil { + return nil, errors.New(fErr.String()) } - return nil + return msgCounter, nil +} + +// check if there is a binding to the remote feature +func (f *Feature) HasBinding() bool { + binding := f.featureLocal.HasBindingToRemote(f.featureRemote.Address()) + return binding } // bind to the feature of the entity -func (f *FeatureImpl) Bind() error { - if f.featureRemote == nil { - return errors.New("remote feature not available") - } - if _, fErr := f.featureLocal.BindToRemote(f.featureRemote.Address()); fErr != nil { - return errors.New(fErr.String()) +func (f *Feature) Bind() (*model.MsgCounterType, error) { + msgCounter, fErr := f.featureLocal.BindToRemote(f.featureRemote.Address()) + if fErr != nil { + return nil, errors.New(fErr.String()) } - return nil + return msgCounter, nil } -func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) { +// add a callback function to be invoked once a result to a msgCounter came in +func (f *Feature) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) { f.featureLocal.AddResultCallback(msgCounterReference, function) } @@ -74,7 +92,7 @@ func (f *FeatureImpl) AddResultCallback(msgCounterReference model.MsgCounterType // selectors and elements are used if specific data should be requested by using // model.FilterType DataSelectors (selectors) and/or DataElements (elements) // both should use the proper data types for the used function -func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, elements any) (*model.MsgCounterType, error) { +func (f *Feature) requestData(function model.FunctionType, selectors any, elements any) (*model.MsgCounterType, error) { if f.featureRemote == nil { return nil, ErrDataNotAvailable } @@ -97,11 +115,7 @@ func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, el } // internal helper method for getting local and remote feature for a given featureType and a given remoteDevice -func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (api.FeatureLocalInterface, api.FeatureRemoteInterface, error) { - if f.remoteEntity == nil { - return nil, nil, errors.New("invalid remote entity provided") - } - +func (f *Feature) getLocalClientAndRemoteServerFeatures() (api.FeatureLocalInterface, api.FeatureRemoteInterface, error) { featureLocal := f.localEntity.FeatureOfTypeAndRole(f.featureType, f.localRole) featureRemote := f.remoteEntity.Device().FeatureByEntityTypeAndRole(f.remoteEntity, f.featureType, f.remoteRole) diff --git a/features/identification.go b/features/identification.go index 3c1051f7..f47269f0 100644 --- a/features/identification.go +++ b/features/identification.go @@ -7,20 +7,20 @@ import ( ) type Identification struct { - *FeatureImpl + *Feature } func NewIdentification( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*Identification, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeIdentification, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeIdentification, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } i := &Identification{ - FeatureImpl: feature, + Feature: feature, } return i, nil diff --git a/features/incentivetable.go b/features/incentivetable.go index 13b8a6e1..a8467403 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -7,35 +7,33 @@ import ( ) type IncentiveTable struct { - *FeatureImpl + *Feature } func NewIncentiveTable( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*IncentiveTable, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } i := &IncentiveTable{ - FeatureImpl: feature, + Feature: feature, } return i, nil } // request FunctionTypeIncentiveTableDescriptionData from a remote entity -func (i *IncentiveTable) RequestDescriptions() error { - _, err := i.requestData(model.FunctionTypeIncentiveTableDescriptionData, nil, nil) - return err +func (i *IncentiveTable) RequestDescriptions() (*model.MsgCounterType, error) { + return i.requestData(model.FunctionTypeIncentiveTableDescriptionData, nil, nil) } // request FunctionTypeIncentiveTableConstraintsData from a remote entity -func (i *IncentiveTable) RequestConstraints() error { - _, err := i.requestData(model.FunctionTypeIncentiveTableConstraintsData, nil, nil) - return err +func (i *IncentiveTable) RequestConstraints() (*model.MsgCounterType, error) { + return i.requestData(model.FunctionTypeIncentiveTableConstraintsData, nil, nil) } // request FunctionTypeIncentiveTableData from a remote entity diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 6f1b1bc4..5b73a7a4 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -55,13 +55,15 @@ func (s *IncentiveTableSuite) BeforeTest(suiteName, testName string) { } func (s *IncentiveTableSuite) Test_RequestDescriptions() { - err := s.incentiveTable.RequestDescriptions() + counter, err := s.incentiveTable.RequestDescriptions() assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) } func (s *IncentiveTableSuite) Test_RequestConstraints() { - err := s.incentiveTable.RequestConstraints() + counter, err := s.incentiveTable.RequestConstraints() assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) } func (s *IncentiveTableSuite) Test_RequestValues() { diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 81457e96..916b24fa 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -7,35 +7,33 @@ import ( ) type LoadControl struct { - *FeatureImpl + *Feature } func NewLoadControl( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*LoadControl, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeLoadControl, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeLoadControl, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } lc := &LoadControl{ - FeatureImpl: feature, + Feature: feature, } return lc, nil } // request FunctionTypeLoadControlLimitDescriptionListData from a remote device -func (l *LoadControl) RequestLimitDescriptions() error { - _, err := l.requestData(model.FunctionTypeLoadControlLimitDescriptionListData, nil, nil) - return err +func (l *LoadControl) RequestLimitDescriptions() (*model.MsgCounterType, error) { + return l.requestData(model.FunctionTypeLoadControlLimitDescriptionListData, nil, nil) } // request FunctionTypeLoadControlLimitConstraintsListData from a remote device -func (l *LoadControl) RequestLimitConstraints() error { - _, err := l.requestData(model.FunctionTypeLoadControlLimitConstraintsListData, nil, nil) - return err +func (l *LoadControl) RequestLimitConstraints() (*model.MsgCounterType, error) { + return l.requestData(model.FunctionTypeLoadControlLimitConstraintsListData, nil, nil) } // request FunctionTypeLoadControlLimitListData from a remote device diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index e3abfaff..86b463b9 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -54,24 +54,16 @@ func (s *LoadControlSuite) BeforeTest(suiteName, testName string) { assert.NotNil(s.T(), s.loadControl) } -func (s *LoadControlSuite) Test_Bind() { - err := s.loadControl.Bind() - assert.Nil(s.T(), err) -} - -func (s *LoadControlSuite) Test_Subscribe() { - err := s.loadControl.Subscribe() - assert.Nil(s.T(), err) -} - func (s *LoadControlSuite) Test_RequestLimitDescription() { - err := s.loadControl.RequestLimitDescriptions() + counter, err := s.loadControl.RequestLimitDescriptions() assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) } func (s *LoadControlSuite) Test_RequestLimitConstraints() { - err := s.loadControl.RequestLimitConstraints() + counter, err := s.loadControl.RequestLimitConstraints() assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) } func (s *LoadControlSuite) Test_RequestLimits() { diff --git a/features/measurement.go b/features/measurement.go index e8659611..ec21ea74 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -7,20 +7,20 @@ import ( ) type Measurement struct { - *FeatureImpl + *Feature } func NewMeasurement( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*Measurement, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeMeasurement, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeMeasurement, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } m := &Measurement{ - FeatureImpl: feature, + Feature: feature, } return m, nil diff --git a/features/timeseries.go b/features/timeseries.go index dcd25130..b91bbe45 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -7,20 +7,20 @@ import ( ) type TimeSeries struct { - *FeatureImpl + *Feature } func NewTimeSeries( localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*TimeSeries, error) { - feature, err := NewFeatureImpl(model.FeatureTypeTypeTimeSeries, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeTimeSeries, localRole, remoteRole, localEntity, remoteEntity) if err != nil { return nil, err } t := &TimeSeries{ - FeatureImpl: feature, + Feature: feature, } return t, nil diff --git a/go.mod b/go.mod index 05a0c323..2a2d5c8d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 - github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f + github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444 github.com/stretchr/testify v1.8.4 ) @@ -34,3 +34,6 @@ retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. ) + +// replace github.com/enbility/ship-go => ../ship-go +// replace github.com/enbility/spine-go => ../spine-go diff --git a/go.sum b/go.sum index 76fc8325..92fb332c 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 h1:BmevZOzjfBjGFB4U8iYPgnY8zDhDJbAODksJ5tzLRfg= github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f h1:VRQSmiN661D+p433V3ufgpg2vI58SJcCnXUbyA1vwn8= -github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= +github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444 h1:D9nrLCu8CHVDlchyjkWRAsUPFbA+ELpDSh4sp/acACE= +github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From fe90d8afcea288957a42487a5d9ca8218e3ac241 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 21 Feb 2024 12:36:01 +0100 Subject: [PATCH 197/240] Update ship and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2a2d5c8d..0c042869 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 - github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444 + github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 + github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 92fb332c..2e0811e7 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 h1:BmevZOzjfBjGFB4U8iYPgnY8zDhDJbAODksJ5tzLRfg= -github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444 h1:D9nrLCu8CHVDlchyjkWRAsUPFbA+ELpDSh4sp/acACE= -github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= +github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= +github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718 h1:qX9JNqBrxH8COazT+jnLpr7vEZt/8CVshy0r+k9uQcM= +github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 2779e0cf6e2eaac65ed77b5af0430e4349fc418c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 21 Feb 2024 12:36:29 +0100 Subject: [PATCH 198/240] Add support for (remote) generic client features --- features/feature.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/features/feature.go b/features/feature.go index 33ada565..024fc3fb 100644 --- a/features/feature.go +++ b/features/feature.go @@ -117,12 +117,18 @@ func (f *Feature) requestData(function model.FunctionType, selectors any, elemen // internal helper method for getting local and remote feature for a given featureType and a given remoteDevice func (f *Feature) getLocalClientAndRemoteServerFeatures() (api.FeatureLocalInterface, api.FeatureRemoteInterface, error) { featureLocal := f.localEntity.FeatureOfTypeAndRole(f.featureType, f.localRole) - featureRemote := f.remoteEntity.Device().FeatureByEntityTypeAndRole(f.remoteEntity, f.featureType, f.remoteRole) - + if featureLocal == nil && f.localRole == model.RoleTypeClient { + featureLocal = f.localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeGeneric, f.localRole) + } if featureLocal == nil { return nil, nil, errors.New("local feature not found") } + featureRemote := f.remoteEntity.Device().FeatureByEntityTypeAndRole(f.remoteEntity, f.featureType, f.remoteRole) + if featureRemote == nil && f.localRole == model.RoleTypeClient { + featureRemote = f.remoteEntity.Device().FeatureByEntityTypeAndRole(f.remoteEntity, model.FeatureTypeTypeGeneric, f.localRole) + } + if featureRemote == nil { return nil, nil, errors.New("remote feature not found") } From 0333078562a11e1b937769ebb318a80be75d104d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 21 Feb 2024 12:36:56 +0100 Subject: [PATCH 199/240] Add feature tests --- features/feature_test.go | 110 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 features/feature_test.go diff --git a/features/feature_test.go b/features/feature_test.go new file mode 100644 index 00000000..8ae528e5 --- /dev/null +++ b/features/feature_test.go @@ -0,0 +1,110 @@ +package features_test + +import ( + "testing" + + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestFeatureSuite(t *testing.T) { + suite.Run(t, new(FeatureSuite)) +} + +type FeatureSuite struct { + suite.Suite + + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface + + testFeature *features.Feature + sentMessage []byte +} + +var _ shipapi.ShipConnectionDataWriterInterface = (*FeatureSuite)(nil) + +func (s *FeatureSuite) WriteShipMessageWithPayload(message []byte) { + s.sentMessage = message +} + +func (s *FeatureSuite) BeforeTest(suiteName, testName string) { + s.localEntity, s.remoteEntity = setupFeatures( + s.T(), + s, + []featureFunctions{ + { + featureType: model.FeatureTypeTypeAlarm, + functions: []model.FunctionType{ + model.FunctionTypeAlarmListData, + }, + }, + }, + ) + + var err error + s.testFeature, err = features.NewFeature(model.FeatureTypeTypeAlarm, model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), s.testFeature) +} + +func (s *FeatureSuite) Test_NewFeature() { + newFeature, err := features.NewFeature(model.FeatureTypeTypeBill, model.RoleTypeClient, model.RoleTypeServer, nil, s.remoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), newFeature) + + newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, model.RoleTypeClient, model.RoleTypeServer, s.localEntity, nil) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), newFeature) + + newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + assert.NotNil(s.T(), err) + assert.NotNil(s.T(), newFeature) + + f := spine.NewFeatureLocal(1, s.localEntity, model.FeatureTypeTypeBill, model.RoleTypeClient) + s.localEntity.AddFeature(f) + + newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + assert.NotNil(s.T(), err) + assert.NotNil(s.T(), newFeature) +} + +func (s *FeatureSuite) Test_Subscription() { + subscription := s.testFeature.HasSubscription() + assert.Equal(s.T(), false, subscription) + + counter, err := s.testFeature.Subscribe() + assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) + + subscription = s.testFeature.HasSubscription() + assert.Equal(s.T(), true, subscription) + + counter, err = s.testFeature.Subscribe() + assert.NotNil(s.T(), counter) + assert.Nil(s.T(), err) +} + +func (s *FeatureSuite) Test_Binding() { + binding := s.testFeature.HasBinding() + assert.Equal(s.T(), false, binding) + + counter, err := s.testFeature.Bind() + assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) + + binding = s.testFeature.HasBinding() + assert.Equal(s.T(), true, binding) + + counter, err = s.testFeature.Bind() + assert.NotNil(s.T(), counter) + assert.Nil(s.T(), err) +} + +func (s *FeatureSuite) Test_ResultCallback() { + s.testFeature.AddResultCallback(10, func(msg spineapi.ResultMessage) {}) +} From bd5bc5acd30efbf3e2440f36f24e5ebfb1a91dba Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 18:21:56 +0100 Subject: [PATCH 200/240] Update features --- features/electricalconnection.go | 15 +++++++++++ features/electricalconnection_test.go | 36 +++++++++++++++++++++++++++ features/measurement.go | 33 ++++++++++++++++++------ features/measurement_test.go | 24 ++++++++++++++++-- features/timeseries.go | 10 +++----- features/timeseries_test.go | 6 +++-- 6 files changed, 107 insertions(+), 17 deletions(-) diff --git a/features/electricalconnection.go b/features/electricalconnection.go index 9b4e02e8..b577f901 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -41,6 +41,11 @@ func (e *ElectricalConnection) RequestPermittedValueSets() (*model.MsgCounterTyp return e.requestData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, nil, nil) } +// request FunctionTypeElectricalConnectionCharacteristicListData from a remote entity +func (e *ElectricalConnection) RequestCharacteristics() (*model.MsgCounterType, error) { + return e.requestData(model.FunctionTypeElectricalConnectionCharacteristicListData, nil, nil) +} + // return list of description for Electrical Connection func (e *ElectricalConnection) GetDescriptions() ([]model.ElectricalConnectionDescriptionDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionDescriptionListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionDescriptionListData) @@ -278,3 +283,13 @@ func (e *ElectricalConnection) AdjustValueToBeWithinPermittedValuesForParameter( return value } + +// return parameter descriptions for all Electrical Connections +func (e *ElectricalConnection) GetCharacteristics() ([]model.ElectricalConnectionCharacteristicDataType, error) { + data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionParameterDescriptionListData) + if err != nil { + return nil, ErrDataNotAvailable + } + + return data.ElectricalConnectionCharacteristicListData, nil +} diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 8dd09567..27558009 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -72,6 +72,12 @@ func (s *ElectricalConnectionSuite) Test_RequestPermittedValueSets() { assert.NotNil(s.T(), counter) } +func (s *ElectricalConnectionSuite) Test_RequestCharacteristics() { + counter, err := s.electricalConnection.RequestCharacteristics() + assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) +} + func (s *ElectricalConnectionSuite) Test_GetDescriptions() { data, err := s.electricalConnection.GetDescriptions() assert.NotNil(s.T(), err) @@ -291,6 +297,18 @@ func (s *ElectricalConnectionSuite) Test_AdjustValueToBeWithinPermittedValuesFor assert.Equal(s.T(), value, 0.1) } +func (s *ElectricalConnectionSuite) Test_GetCharacteristics() { + data, err := s.electricalConnection.GetCharacteristics() + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + s.addCharacteristics() + + data, err = s.electricalConnection.GetCharacteristics() + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) +} + // helper func (s *ElectricalConnectionSuite) addDescription() { @@ -308,6 +326,24 @@ func (s *ElectricalConnectionSuite) addDescription() { rF.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, fData, nil, nil) } +func (s *ElectricalConnectionSuite) addCharacteristics() { + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) + fData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), + CharacteristicId: util.Ptr(model.ElectricalConnectionCharaceteristicIdType(0)), + CharacteristicContext: util.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: util.Ptr(model.ElectricalConnectionCharacteristicTypeTypeEnergyCapacityNominalMax), + Value: model.NewScaledNumberType(98), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + }, + }, + } + rF.UpdateData(model.FunctionTypeElectricalConnectionCharacteristicListData, fData, nil, nil) +} + func (s *ElectricalConnectionSuite) addParamDescriptionCurrents() { rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.ElectricalConnectionParameterDescriptionListDataType{ diff --git a/features/measurement.go b/features/measurement.go index ec21ea74..b7b181ef 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -27,16 +27,13 @@ func NewMeasurement( } // request FunctionTypeMeasurementDescriptionListData from a remote device -func (m *Measurement) RequestDescriptions() error { - _, err := m.requestData(model.FunctionTypeMeasurementDescriptionListData, nil, nil) - - return err +func (m *Measurement) RequestDescriptions() (*model.MsgCounterType, error) { + return m.requestData(model.FunctionTypeMeasurementDescriptionListData, nil, nil) } // request FunctionTypeMeasurementConstraintsListData from a remote entity -func (m *Measurement) RequestConstraints() error { - _, err := m.requestData(model.FunctionTypeMeasurementConstraintsListData, nil, nil) - return err +func (m *Measurement) RequestConstraints() (*model.MsgCounterType, error) { + return m.requestData(model.FunctionTypeMeasurementConstraintsListData, nil, nil) } // request FunctionTypeMeasurementListData from a remote entity @@ -104,6 +101,28 @@ func (m *Measurement) GetValues() ([]model.MeasurementDataType, error) { return data.MeasurementData, nil } +// return current values of a measurementId +// +// if nothing is found, it will return an error +func (m *Measurement) GetValueForMeasurementId(id model.MeasurementIdType) (float64, error) { + values, err := m.GetValues() + if err != nil { + return 0, err + } + + for _, item := range values { + if item.MeasurementId == nil || item.Value == nil { + continue + } + + if *item.MeasurementId == id { + return item.Value.GetValue(), nil + } + } + + return 0, ErrDataNotAvailable +} + // return current values of a defined measurementType, commodityType and scopeType // // if nothing is found, it will return an error diff --git a/features/measurement_test.go b/features/measurement_test.go index 804a069e..1e50f06a 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -64,13 +64,15 @@ func (s *MeasurementSuite) BeforeTest(suiteName, testName string) { } func (s *MeasurementSuite) Test_RequestDescriptions() { - err := s.measurement.RequestDescriptions() + msgCounter, err := s.measurement.RequestDescriptions() assert.Nil(s.T(), err) + assert.NotNil(s.T(), msgCounter) } func (s *MeasurementSuite) Test_RequestConstraints() { - err := s.measurement.RequestConstraints() + msgCounter, err := s.measurement.RequestConstraints() assert.Nil(s.T(), err) + assert.NotNil(s.T(), msgCounter) } func (s *MeasurementSuite) Test_RequestValues() { @@ -79,6 +81,24 @@ func (s *MeasurementSuite) Test_RequestValues() { assert.NotNil(s.T(), counter) } +func (s *MeasurementSuite) Test_GetValueForMeasurementId() { + measurement := model.MeasurementIdType(0) + + value, err := s.measurement.GetValueForMeasurementId(measurement) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, value) + + s.addData() + + value, err = s.measurement.GetValueForMeasurementId(measurement) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 9.0, value) + + value, err = s.measurement.GetValueForMeasurementId(model.MeasurementIdType(100)) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, value) +} + func (s *MeasurementSuite) Test_GetValuesForTypeCommodityScope() { measurement := model.MeasurementTypeTypeCurrent commodity := model.CommodityTypeTypeElectricity diff --git a/features/timeseries.go b/features/timeseries.go index b91bbe45..c27df6a1 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -27,15 +27,13 @@ func NewTimeSeries( } // request FunctionTypeTimeSeriesDescriptionListData from a remote entity -func (t *TimeSeries) RequestDescriptions() error { - _, err := t.requestData(model.FunctionTypeTimeSeriesDescriptionListData, nil, nil) - return err +func (t *TimeSeries) RequestDescriptions() (*model.MsgCounterType, error) { + return t.requestData(model.FunctionTypeTimeSeriesDescriptionListData, nil, nil) } // request FunctionTypeTimeSeriesConstraintsListData from a remote entity -func (t *TimeSeries) RequestConstraints() error { - _, err := t.requestData(model.FunctionTypeTimeSeriesConstraintsListData, nil, nil) - return err +func (t *TimeSeries) RequestConstraints() (*model.MsgCounterType, error) { + return t.requestData(model.FunctionTypeTimeSeriesConstraintsListData, nil, nil) } // request FunctionTypeTimeSeriesListData from a remote device diff --git a/features/timeseries_test.go b/features/timeseries_test.go index e625dc3c..7e32fb80 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -56,13 +56,15 @@ func (s *TimeSeriesSuite) BeforeTest(suiteName, testName string) { } func (s *TimeSeriesSuite) Test_RequestDescription() { - err := s.timeSeries.RequestDescriptions() + msgCounter, err := s.timeSeries.RequestDescriptions() assert.Nil(s.T(), err) + assert.NotNil(s.T(), msgCounter) } func (s *TimeSeriesSuite) Test_RequestConstraints() { - err := s.timeSeries.RequestConstraints() + msgCounter, err := s.timeSeries.RequestConstraints() assert.Nil(s.T(), err) + assert.NotNil(s.T(), msgCounter) } func (s *TimeSeriesSuite) Test_RequestValues() { From 444cee6d0e5069995197624385f359e313bbfd23 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 18:31:24 +0100 Subject: [PATCH 201/240] Fix electrical connection getCharacteristics --- features/electricalconnection.go | 2 +- features/electricalconnection_test.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/features/electricalconnection.go b/features/electricalconnection.go index b577f901..300318a5 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -286,7 +286,7 @@ func (e *ElectricalConnection) AdjustValueToBeWithinPermittedValuesForParameter( // return parameter descriptions for all Electrical Connections func (e *ElectricalConnection) GetCharacteristics() ([]model.ElectricalConnectionCharacteristicDataType, error) { - data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionParameterDescriptionListData) + data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionCharacteristicListData) if err != nil { return nil, ErrDataNotAvailable } diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 27558009..427d8a63 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -43,6 +43,7 @@ func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) { model.FunctionTypeElectricalConnectionDescriptionListData, model.FunctionTypeElectricalConnectionParameterDescriptionListData, model.FunctionTypeElectricalConnectionPermittedValueSetListData, + model.FunctionTypeElectricalConnectionCharacteristicListData, }, }, }, @@ -121,6 +122,28 @@ func (s *ElectricalConnectionSuite) Test_GetParameterDescriptions() { assert.NotNil(s.T(), data) } +func (s *ElectricalConnectionSuite) Test_GetParameterDescriptionForScope() { + data, err := s.electricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPowerTotal) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + s.addDescription() + + data, err = s.electricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPowerTotal) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + s.addParamDescriptionPower() + + data, err = s.electricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPowerTotal) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) + + data, err = s.electricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACCurrent) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) +} + func (s *ElectricalConnectionSuite) Test_GetParameterDescriptionForParameterId() { parametertId := model.ElectricalConnectionParameterIdType(1) data, err := s.electricalConnection.GetParameterDescriptionForParameterId(parametertId) From 3f45bddf9e001dd0a0d783c5bac0bc5b22f40813 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 21:13:21 +0100 Subject: [PATCH 202/240] Update spine --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0c042869..721d6d30 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 - github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718 + github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 2e0811e7..5e7232da 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718 h1:qX9JNqBrxH8COazT+jnLpr7vEZt/8CVshy0r+k9uQcM= -github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6 h1:q87C8Fb76kRbbMqG3vCM52yxjYxbclY23+rbBo2nq+Y= +github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 969422b32a730427f27c9702ac244c239e059c6e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 18:56:58 +0100 Subject: [PATCH 203/240] Update spine --- go.mod | 5 +---- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 721d6d30..8acd8dc3 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 - github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6 + github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def github.com/stretchr/testify v1.8.4 ) @@ -34,6 +34,3 @@ retract ( v0.2.2 // Contains retractions only. v0.2.1 // Published accidentally. ) - -// replace github.com/enbility/ship-go => ../ship-go -// replace github.com/enbility/spine-go => ../spine-go diff --git a/go.sum b/go.sum index 5e7232da..654a6f3a 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6 h1:q87C8Fb76kRbbMqG3vCM52yxjYxbclY23+rbBo2nq+Y= -github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def h1:glevkOCgXd7IBiu69mTvv7sxgYc8W6B37yDBfcVYqhs= +github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 2fe6960db55dd301159710e8110eca6e2863537a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 18:57:14 +0100 Subject: [PATCH 204/240] Add more feature helpers --- features/electricalconnection.go | 25 +++++++++++++++- features/electricalconnection_test.go | 22 ++++++++++++++ features/loadcontrol.go | 36 +++++++++++++++++++++++ features/loadcontrol_test.go | 41 +++++++++++++++++++++++---- 4 files changed, 118 insertions(+), 6 deletions(-) diff --git a/features/electricalconnection.go b/features/electricalconnection.go index 300318a5..d8bca79b 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -284,7 +284,7 @@ func (e *ElectricalConnection) AdjustValueToBeWithinPermittedValuesForParameter( return value } -// return parameter descriptions for all Electrical Connections +// return characteristics for a Electrical Connections func (e *ElectricalConnection) GetCharacteristics() ([]model.ElectricalConnectionCharacteristicDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionCharacteristicListData) if err != nil { @@ -293,3 +293,26 @@ func (e *ElectricalConnection) GetCharacteristics() ([]model.ElectricalConnectio return data.ElectricalConnectionCharacteristicListData, nil } + +// return characteristics for a Electrical Connections +func (e *ElectricalConnection) GetCharacteristicForContextType( + context model.ElectricalConnectionCharacteristicContextType, + cType model.ElectricalConnectionCharacteristicTypeType, +) (*model.ElectricalConnectionCharacteristicDataType, error) { + data, err := e.GetCharacteristics() + if err != nil || data == nil || len(data) == 0 { + return nil, ErrDataNotAvailable + } + + for _, item := range data { + if item.CharacteristicId != nil && + item.CharacteristicContext != nil && + *item.CharacteristicContext == context && + item.CharacteristicType != nil && + *item.CharacteristicType == cType { + return &item, nil + } + } + + return nil, ErrDataNotAvailable +} diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 427d8a63..92c0c881 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -332,6 +332,28 @@ func (s *ElectricalConnectionSuite) Test_GetCharacteristics() { assert.NotNil(s.T(), data) } +func (s *ElectricalConnectionSuite) Test_GetCharacteristicForContextType() { + data, err := s.electricalConnection.GetCharacteristicForContextType( + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypeEnergyCapacityNominalMax) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + s.addCharacteristics() + + data, err = s.electricalConnection.GetCharacteristicForContextType( + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypeApparentPowerConsumptionNominalMax) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.electricalConnection.GetCharacteristicForContextType( + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypeEnergyCapacityNominalMax) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) +} + // helper func (s *ElectricalConnectionSuite) addDescription() { diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 916b24fa..44688716 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -75,6 +75,42 @@ func (l *LoadControl) GetLimitDescriptionsForCategory(category model.LoadControl return result, nil } +// returns the load control limit descriptions of a provided type, direction and scope +// returns an error if no description data for the category is available +func (l *LoadControl) GetLimitDescriptionsForCategoryTypeDirectionScope( + limitType model.LoadControlLimitTypeType, + limitCategory model.LoadControlCategoryType, + limitDirection model.EnergyDirectionType, + scope model.ScopeTypeType, +) ([]model.LoadControlLimitDescriptionDataType, error) { + data, err := l.GetLimitDescriptions() + if err != nil || len(data) == 0 { + return nil, err + } + + var result []model.LoadControlLimitDescriptionDataType + + for _, item := range data { + if item.LimitId != nil && + item.LimitType != nil && + *item.LimitType == limitType && + item.LimitCategory != nil && + *item.LimitCategory == limitCategory && + item.LimitDirection != nil && + *item.LimitDirection == limitDirection && + item.ScopeType != nil && + *item.ScopeType == scope { + result = append(result, item) + } + } + + if len(result) == 0 { + return nil, ErrDataNotAvailable + } + + return result, nil +} + // returns the load control limit descriptions for a provided measurementId // returns an error if no description data for the measurementId is available func (l *LoadControl) GetLimitDescriptionsForMeasurementId(measurementId model.MeasurementIdType) ([]model.LoadControlLimitDescriptionDataType, error) { diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index 86b463b9..a9093d8d 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -100,6 +100,34 @@ func (s *LoadControlSuite) Test_GetLimitDescriptionsForCategory() { assert.NotNil(s.T(), data) } +func (s *LoadControlSuite) Test_GetLimitDescriptionsForTypeDirectionScope() { + data, err := s.loadControl.GetLimitDescriptionsForCategoryTypeDirectionScope( + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + s.addDescription() + + data, err = s.loadControl.GetLimitDescriptionsForCategoryTypeDirectionScope( + model.LoadControlLimitTypeTypeMaxValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.loadControl.GetLimitDescriptionsForCategoryTypeDirectionScope( + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) +} + func (s *LoadControlSuite) Test_GetLimitDescriptionsForMeasurementId() { measurementId := model.MeasurementIdType(0) data, err := s.loadControl.GetLimitDescriptionsForMeasurementId(measurementId) @@ -109,8 +137,8 @@ func (s *LoadControlSuite) Test_GetLimitDescriptionsForMeasurementId() { s.addDescription() data, err = s.loadControl.GetLimitDescriptionsForMeasurementId(measurementId) - assert.Nil(s.T(), err) - assert.NotNil(s.T(), data) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) measurementId = model.MeasurementIdType(10) data, err = s.loadControl.GetLimitDescriptionsForMeasurementId(measurementId) @@ -188,9 +216,12 @@ func (s *LoadControlSuite) addDescription() { fData := &model.LoadControlLimitDescriptionListDataType{ LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ { - LimitId: util.Ptr(model.LoadControlLimitIdType(0)), - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - LimitCategory: util.Ptr(model.LoadControlCategoryTypeObligation), + LimitId: util.Ptr(model.LoadControlLimitIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(0)), + LimitType: util.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: util.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: util.Ptr(model.EnergyDirectionTypeConsume), + ScopeType: util.Ptr(model.ScopeTypeTypeActivePowerLimit), }, }, } From e38e7f9f5b47e41629ef1dc6aadb4a46e6ab5b5e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 19:12:08 +0100 Subject: [PATCH 205/240] Fix test --- features/loadcontrol_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index a9093d8d..f1f05223 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -137,8 +137,8 @@ func (s *LoadControlSuite) Test_GetLimitDescriptionsForMeasurementId() { s.addDescription() data, err = s.loadControl.GetLimitDescriptionsForMeasurementId(measurementId) - assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) measurementId = model.MeasurementIdType(10) data, err = s.loadControl.GetLimitDescriptionsForMeasurementId(measurementId) From 8b9b1211f41c5d0084614167f4d645966152f8cd Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 26 Feb 2024 13:38:42 +0100 Subject: [PATCH 206/240] Update spine --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8acd8dc3..35132ef6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 - github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def + github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 654a6f3a..0824603a 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def h1:glevkOCgXd7IBiu69mTvv7sxgYc8W6B37yDBfcVYqhs= -github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 h1:/oXzUGRXtOlQRflZD6nMd3aFOvl2n78u6G1v0IzGz8U= +github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 45c4e2d20a818005278ef7a98f3a107cae3f9a12 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 26 Feb 2024 13:39:02 +0100 Subject: [PATCH 207/240] Add unsubscribe and unbind feature support --- features/feature.go | 22 ++++++++++++++++++++++ features/feature_test.go | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/features/feature.go b/features/feature.go index 024fc3fb..2ee384ca 100644 --- a/features/feature.go +++ b/features/feature.go @@ -67,6 +67,17 @@ func (f *Feature) Subscribe() (*model.MsgCounterType, error) { return msgCounter, nil } +// unssubscribe to the feature of the entity +func (f *Feature) Unsubscribe() (*model.MsgCounterType, error) { + msgCounter, fErr := f.featureLocal.RemoveRemoteSubscription(f.featureRemote.Address()) + + if fErr != nil { + return nil, errors.New(fErr.String()) + } + + return msgCounter, nil +} + // check if there is a binding to the remote feature func (f *Feature) HasBinding() bool { binding := f.featureLocal.HasBindingToRemote(f.featureRemote.Address()) @@ -83,6 +94,17 @@ func (f *Feature) Bind() (*model.MsgCounterType, error) { return msgCounter, nil } +// remove a binding to the feature of the entity +func (f *Feature) Unbind() (*model.MsgCounterType, error) { + msgCounter, fErr := f.featureLocal.RemoveRemoteBinding(f.featureRemote.Address()) + + if fErr != nil { + return nil, errors.New(fErr.String()) + } + + return msgCounter, nil +} + // add a callback function to be invoked once a result to a msgCounter came in func (f *Feature) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) { f.featureLocal.AddResultCallback(msgCounterReference, function) diff --git a/features/feature_test.go b/features/feature_test.go index 8ae528e5..20593785 100644 --- a/features/feature_test.go +++ b/features/feature_test.go @@ -87,6 +87,13 @@ func (s *FeatureSuite) Test_Subscription() { counter, err = s.testFeature.Subscribe() assert.NotNil(s.T(), counter) assert.Nil(s.T(), err) + + counter, err = s.testFeature.Unsubscribe() + assert.NotNil(s.T(), counter) + assert.Nil(s.T(), err) + + subscription = s.testFeature.HasSubscription() + assert.Equal(s.T(), false, subscription) } func (s *FeatureSuite) Test_Binding() { @@ -103,6 +110,13 @@ func (s *FeatureSuite) Test_Binding() { counter, err = s.testFeature.Bind() assert.NotNil(s.T(), counter) assert.Nil(s.T(), err) + + counter, err = s.testFeature.Unbind() + assert.NotNil(s.T(), counter) + assert.Nil(s.T(), err) + + binding = s.testFeature.HasBinding() + assert.Equal(s.T(), false, binding) } func (s *FeatureSuite) Test_ResultCallback() { From 279aff7d8eb18508741fefce982f43deca5862ae Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 26 Feb 2024 18:16:30 +0100 Subject: [PATCH 208/240] Minor refactor to features No need to provide local and remote features role, as they should be client for the local feature and server for the remote feature all the time --- features/api.go | 1 + features/deviceclassification.go | 7 +++++-- features/deviceclassification_test.go | 2 +- features/deviceconfiguration.go | 7 +++++-- features/deviceconfiguration_test.go | 2 +- features/devicediagnosis.go | 7 +++++-- features/devicediagnosis_test.go | 2 +- features/electricalconnection.go | 7 +++++-- features/electricalconnection_test.go | 2 +- features/feature.go | 16 ++++++---------- features/feature_test.go | 10 +++++----- features/identification.go | 7 +++++-- features/identification_test.go | 2 +- features/incentivetable.go | 7 +++++-- features/incentivetable_test.go | 2 +- features/loadcontrol.go | 7 +++++-- features/loadcontrol_test.go | 2 +- features/measurement.go | 7 +++++-- features/measurement_test.go | 2 +- features/timeseries.go | 7 +++++-- features/timeseries_test.go | 2 +- integration_tests/emobility_measurement_test.go | 4 ++-- 22 files changed, 68 insertions(+), 44 deletions(-) diff --git a/features/api.go b/features/api.go index 1f281c7d..9d3f0149 100644 --- a/features/api.go +++ b/features/api.go @@ -5,6 +5,7 @@ import ( "github.com/enbility/spine-go/model" ) +// Feature interface were the local feature role is client and the remote feature role is server type FeatureInterface interface { // check if there is a subscription to the remote feature HasSubscription() bool diff --git a/features/deviceclassification.go b/features/deviceclassification.go index 82ce3190..287f5b02 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -10,11 +10,14 @@ type DeviceClassification struct { *Feature } +// Get a new DeviceClassification features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewDeviceClassification( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*DeviceClassification, error) { - feature, err := NewFeature(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeDeviceClassification, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/deviceclassification_test.go b/features/deviceclassification_test.go index fdc3c083..d6c442ac 100644 --- a/features/deviceclassification_test.go +++ b/features/deviceclassification_test.go @@ -47,7 +47,7 @@ func (s *DeviceClassificationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceClassification, err = features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.deviceClassification, err = features.NewDeviceClassification(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceClassification) } diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index c5ae264e..82730c89 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -10,11 +10,14 @@ type DeviceConfiguration struct { *Feature } +// Get a new DeviceConfiguration features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewDeviceConfiguration( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*DeviceConfiguration, error) { - feature, err := NewFeature(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeDeviceConfiguration, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index 2f009add..45af473c 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -46,7 +46,7 @@ func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceConfiguration, err = features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.deviceConfiguration, err = features.NewDeviceConfiguration(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceConfiguration) } diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index 2fb09eaa..a1214630 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -12,11 +12,14 @@ type DeviceDiagnosis struct { *Feature } +// Get a new DeviceDiagnosis features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewDeviceDiagnosis( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*DeviceDiagnosis, error) { - feature, err := NewFeature(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeDeviceDiagnosis, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 4bd43769..59fae296 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -49,7 +49,7 @@ func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) { ) var err error - s.deviceDiagnosis, err = features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.deviceDiagnosis, err = features.NewDeviceDiagnosis(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.deviceDiagnosis) } diff --git a/features/electricalconnection.go b/features/electricalconnection.go index d8bca79b..62a92619 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -10,11 +10,14 @@ type ElectricalConnection struct { *Feature } +// Get a new ElectricalConnection features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewElectricalConnection( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*ElectricalConnection, error) { - feature, err := NewFeature(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeElectricalConnection, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 92c0c881..3c99f407 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -50,7 +50,7 @@ func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) { ) var err error - s.electricalConnection, err = features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.electricalConnection, err = features.NewElectricalConnection(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.electricalConnection) } diff --git a/features/feature.go b/features/feature.go index 2ee384ca..1ec4056c 100644 --- a/features/feature.go +++ b/features/feature.go @@ -25,7 +25,7 @@ type Feature struct { var _ FeatureInterface = (*Feature)(nil) -func NewFeature(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*Feature, error) { +func NewFeature(featureType model.FeatureTypeType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*Feature, error) { if localEntity == nil { return nil, errors.New("local entity is nil") } @@ -36,8 +36,8 @@ func NewFeature(featureType model.FeatureTypeType, localRole, remoteRole model.R f := &Feature{ featureType: featureType, - localRole: localRole, - remoteRole: remoteRole, + localRole: model.RoleTypeClient, + remoteRole: model.RoleTypeServer, spineLocalDevice: localEntity.Device(), localEntity: localEntity, remoteDevice: remoteEntity.Device(), @@ -45,7 +45,7 @@ func NewFeature(featureType model.FeatureTypeType, localRole, remoteRole model.R } var err error - f.featureLocal, f.featureRemote, err = f.getLocalClientAndRemoteServerFeatures() + f.featureLocal, f.featureRemote, err = f.getLocalAndRemoteFeatures() return f, err } @@ -137,9 +137,9 @@ func (f *Feature) requestData(function model.FunctionType, selectors any, elemen } // internal helper method for getting local and remote feature for a given featureType and a given remoteDevice -func (f *Feature) getLocalClientAndRemoteServerFeatures() (api.FeatureLocalInterface, api.FeatureRemoteInterface, error) { +func (f *Feature) getLocalAndRemoteFeatures() (api.FeatureLocalInterface, api.FeatureRemoteInterface, error) { featureLocal := f.localEntity.FeatureOfTypeAndRole(f.featureType, f.localRole) - if featureLocal == nil && f.localRole == model.RoleTypeClient { + if featureLocal == nil { featureLocal = f.localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeGeneric, f.localRole) } if featureLocal == nil { @@ -147,10 +147,6 @@ func (f *Feature) getLocalClientAndRemoteServerFeatures() (api.FeatureLocalInter } featureRemote := f.remoteEntity.Device().FeatureByEntityTypeAndRole(f.remoteEntity, f.featureType, f.remoteRole) - if featureRemote == nil && f.localRole == model.RoleTypeClient { - featureRemote = f.remoteEntity.Device().FeatureByEntityTypeAndRole(f.remoteEntity, model.FeatureTypeTypeGeneric, f.localRole) - } - if featureRemote == nil { return nil, nil, errors.New("remote feature not found") } diff --git a/features/feature_test.go b/features/feature_test.go index 20593785..aef2d65b 100644 --- a/features/feature_test.go +++ b/features/feature_test.go @@ -47,28 +47,28 @@ func (s *FeatureSuite) BeforeTest(suiteName, testName string) { ) var err error - s.testFeature, err = features.NewFeature(model.FeatureTypeTypeAlarm, model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.testFeature, err = features.NewFeature(model.FeatureTypeTypeAlarm, s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.testFeature) } func (s *FeatureSuite) Test_NewFeature() { - newFeature, err := features.NewFeature(model.FeatureTypeTypeBill, model.RoleTypeClient, model.RoleTypeServer, nil, s.remoteEntity) + newFeature, err := features.NewFeature(model.FeatureTypeTypeBill, nil, s.remoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), newFeature) - newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, model.RoleTypeClient, model.RoleTypeServer, s.localEntity, nil) + newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, s.localEntity, nil) assert.NotNil(s.T(), err) assert.Nil(s.T(), newFeature) - newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, s.localEntity, s.remoteEntity) assert.NotNil(s.T(), err) assert.NotNil(s.T(), newFeature) f := spine.NewFeatureLocal(1, s.localEntity, model.FeatureTypeTypeBill, model.RoleTypeClient) s.localEntity.AddFeature(f) - newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + newFeature, err = features.NewFeature(model.FeatureTypeTypeBill, s.localEntity, s.remoteEntity) assert.NotNil(s.T(), err) assert.NotNil(s.T(), newFeature) } diff --git a/features/identification.go b/features/identification.go index f47269f0..a2c35d75 100644 --- a/features/identification.go +++ b/features/identification.go @@ -10,11 +10,14 @@ type Identification struct { *Feature } +// Get a new Identification features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewIdentification( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*Identification, error) { - feature, err := NewFeature(model.FeatureTypeTypeIdentification, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeIdentification, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/identification_test.go b/features/identification_test.go index 4f842bc8..b1a97a0e 100644 --- a/features/identification_test.go +++ b/features/identification_test.go @@ -47,7 +47,7 @@ func (s *IdentificationSuite) BeforeTest(suiteName, testName string) { ) var err error - s.identification, err = features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.identification, err = features.NewIdentification(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.identification) } diff --git a/features/incentivetable.go b/features/incentivetable.go index a8467403..e9a45ca5 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -10,11 +10,14 @@ type IncentiveTable struct { *Feature } +// Get a new IncentiveTable features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewIncentiveTable( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*IncentiveTable, error) { - feature, err := NewFeature(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeIncentiveTable, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/incentivetable_test.go b/features/incentivetable_test.go index 5b73a7a4..c74aab6f 100644 --- a/features/incentivetable_test.go +++ b/features/incentivetable_test.go @@ -49,7 +49,7 @@ func (s *IncentiveTableSuite) BeforeTest(suiteName, testName string) { ) var err error - s.incentiveTable, err = features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.incentiveTable, err = features.NewIncentiveTable(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.incentiveTable) } diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 44688716..46e25c73 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -10,11 +10,14 @@ type LoadControl struct { *Feature } +// Get a new LoadControl features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewLoadControl( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*LoadControl, error) { - feature, err := NewFeature(model.FeatureTypeTypeLoadControl, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeLoadControl, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index f1f05223..e0a77ec0 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -49,7 +49,7 @@ func (s *LoadControlSuite) BeforeTest(suiteName, testName string) { ) var err error - s.loadControl, err = features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.loadControl, err = features.NewLoadControl(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.loadControl) } diff --git a/features/measurement.go b/features/measurement.go index b7b181ef..eaa8102f 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -10,11 +10,14 @@ type Measurement struct { *Feature } +// Get a new Measurement features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewMeasurement( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*Measurement, error) { - feature, err := NewFeature(model.FeatureTypeTypeMeasurement, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeMeasurement, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/measurement_test.go b/features/measurement_test.go index 1e50f06a..d9a76d2c 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -58,7 +58,7 @@ func (s *MeasurementSuite) BeforeTest(suiteName, testName string) { ) var err error - s.measurement, err = features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.measurement, err = features.NewMeasurement(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.measurement) } diff --git a/features/timeseries.go b/features/timeseries.go index c27df6a1..8d2a0205 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -10,11 +10,14 @@ type TimeSeries struct { *Feature } +// Get a new TimeSeries features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server func NewTimeSeries( - localRole, remoteRole model.RoleType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*TimeSeries, error) { - feature, err := NewFeature(model.FeatureTypeTypeTimeSeries, localRole, remoteRole, localEntity, remoteEntity) + feature, err := NewFeature(model.FeatureTypeTypeTimeSeries, localEntity, remoteEntity) if err != nil { return nil, err } diff --git a/features/timeseries_test.go b/features/timeseries_test.go index 7e32fb80..a396b31d 100644 --- a/features/timeseries_test.go +++ b/features/timeseries_test.go @@ -50,7 +50,7 @@ func (s *TimeSeriesSuite) BeforeTest(suiteName, testName string) { ) var err error - s.timeSeries, err = features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, s.remoteEntity) + s.timeSeries, err = features.NewTimeSeries(s.localEntity, s.remoteEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), s.timeSeries) } diff --git a/integration_tests/emobility_measurement_test.go b/integration_tests/emobility_measurement_test.go index fc98cee3..fbbb2111 100644 --- a/integration_tests/emobility_measurement_test.go +++ b/integration_tests/emobility_measurement_test.go @@ -62,10 +62,10 @@ func (s *EmobilityMeasurementSuite) TestGetValuesPerPhaseForScope() { assert.NotNil(s.T(), remoteEntity) var err error - s.measurement, err = features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, remoteEntity) + s.measurement, err = features.NewMeasurement(s.localEntity, remoteEntity) assert.Nil(s.T(), err) - s.electricalconnection, err = features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, s.localEntity, remoteEntity) + s.electricalconnection, err = features.NewElectricalConnection(s.localEntity, remoteEntity) assert.Nil(s.T(), err) // Act From bfaaf09b561b29db19fdcdc6ff2570a546923c03 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 26 Feb 2024 18:16:57 +0100 Subject: [PATCH 209/240] Add package overview to README --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e7dd69fb..be794342 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,17 @@ The supported functionality contains: - Support for SHIP 1.0.1 via [ship-go](https://github.com/enbility/ship-go) - Support for SPINE 1.3.0 via [spine-go](https://github.com/enbility/spine-go) - Certificate handling -- mDNS Support, incl. avahi support (recommended) +- mDNS Support, incl. avahi support - Connection (websocket) handling, including reconnection and double connections - Support for handling pairing of devices +## Packages + +- `api`: global API interface definitions and eebus service configuration +- `features`: provides feature helpers with the local SPINE feature having the client role and the remote SPINE feature being the server for easy access to commonly used functions +- `service`: central package which provides access to SHIP and SPINE. Use this to create the EEBUS service, its configuration and connect to remote EEBUS services +- `util`: package with various useful helper functions + ## Usage The included small demo applications do not implement any usecases and thus will end the connection once it reached exchanging usecase information. @@ -93,7 +100,6 @@ This approach has been tested with: - Porsche Mobile Charger Connect - SMA Home Energy Manager 2.0 - ## Interfaces ### Verbose logging From 95d8d9ffbda98d2d2dee5d22738cb1c1a281cd58 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 26 Feb 2024 18:25:09 +0100 Subject: [PATCH 210/240] Move errors to api --- {features => api}/errors.go | 2 +- features/deviceclassification.go | 9 ++++---- features/deviceconfiguration.go | 19 +++++++++-------- features/devicediagnosis.go | 9 ++++---- features/electricalconnection.go | 33 +++++++++++++++--------------- features/feature.go | 35 ++++++++++++++++++++------------ features/identification.go | 9 ++++---- features/incentivetable.go | 19 +++++++++-------- features/loadcontrol.go | 21 ++++++++++--------- features/measurement.go | 21 ++++++++++--------- features/timeseries.go | 21 ++++++++++--------- 11 files changed, 108 insertions(+), 90 deletions(-) rename {features => api}/errors.go (98%) diff --git a/features/errors.go b/api/errors.go similarity index 98% rename from features/errors.go rename to api/errors.go index 39f87c29..59200057 100644 --- a/features/errors.go +++ b/api/errors.go @@ -1,4 +1,4 @@ -package features +package api import "errors" diff --git a/features/deviceclassification.go b/features/deviceclassification.go index 287f5b02..b5fbeb76 100644 --- a/features/deviceclassification.go +++ b/features/deviceclassification.go @@ -1,7 +1,8 @@ package features import ( - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -15,8 +16,8 @@ type DeviceClassification struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewDeviceClassification( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*DeviceClassification, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*DeviceClassification, error) { feature, err := NewFeature(model.FeatureTypeTypeDeviceClassification, localEntity, remoteEntity) if err != nil { return nil, err @@ -38,7 +39,7 @@ func (d *DeviceClassification) RequestManufacturerDetails() (*model.MsgCounterTy func (d *DeviceClassification) GetManufacturerDetails() (*model.DeviceClassificationManufacturerDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceClassificationManufacturerDataType](d.featureRemote, model.FunctionTypeDeviceClassificationManufacturerData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data, nil diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index 82730c89..2842f7cf 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -1,7 +1,8 @@ package features import ( - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -15,8 +16,8 @@ type DeviceConfiguration struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewDeviceConfiguration( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*DeviceConfiguration, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*DeviceConfiguration, error) { feature, err := NewFeature(model.FeatureTypeTypeDeviceConfiguration, localEntity, remoteEntity) if err != nil { return nil, err @@ -43,7 +44,7 @@ func (d *DeviceConfiguration) RequestKeyValues() (*model.MsgCounterType, error) func (d *DeviceConfiguration) GetDescriptions() ([]model.DeviceConfigurationKeyValueDescriptionDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType](d.featureRemote, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.DeviceConfigurationKeyValueDescriptionData, nil @@ -62,7 +63,7 @@ func (d *DeviceConfiguration) GetDescriptionForKeyId(keyId model.DeviceConfigura } } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // returns the description of a provided key name @@ -81,14 +82,14 @@ func (d *DeviceConfiguration) GetDescriptionForKeyName(keyName model.DeviceConfi } } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // return current values for Device Configuration func (d *DeviceConfiguration) GetKeyValues() ([]model.DeviceConfigurationKeyValueDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueListDataType](d.featureRemote, model.FunctionTypeDeviceConfigurationKeyValueListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.DeviceConfigurationKeyValueData, nil @@ -128,10 +129,10 @@ func (d *DeviceConfiguration) GetKeyValueForKeyName(keyname model.DeviceConfigur case model.DeviceConfigurationKeyValueTypeTypeScaledNumber: return item.Value.ScaledNumber, nil default: - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } } } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index a1214630..ce9785e4 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -3,7 +3,8 @@ package features import ( "time" - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -17,8 +18,8 @@ type DeviceDiagnosis struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewDeviceDiagnosis( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*DeviceDiagnosis, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*DeviceDiagnosis, error) { feature, err := NewFeature(model.FeatureTypeTypeDeviceDiagnosis, localEntity, remoteEntity) if err != nil { return nil, err @@ -40,7 +41,7 @@ func (d *DeviceDiagnosis) RequestState() (*model.MsgCounterType, error) { func (d *DeviceDiagnosis) GetState() (*model.DeviceDiagnosisStateDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.DeviceDiagnosisStateDataType](d.featureRemote, model.FunctionTypeDeviceDiagnosisStateData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data, nil diff --git a/features/electricalconnection.go b/features/electricalconnection.go index 62a92619..9673cfcd 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -1,7 +1,8 @@ package features import ( - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -15,8 +16,8 @@ type ElectricalConnection struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewElectricalConnection( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*ElectricalConnection, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*ElectricalConnection, error) { feature, err := NewFeature(model.FeatureTypeTypeElectricalConnection, localEntity, remoteEntity) if err != nil { return nil, err @@ -53,7 +54,7 @@ func (e *ElectricalConnection) RequestCharacteristics() (*model.MsgCounterType, func (e *ElectricalConnection) GetDescriptions() ([]model.ElectricalConnectionDescriptionDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionDescriptionListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionDescriptionListData) if err != nil { - return nil, ErrMetadataNotAvailable + return nil, api.ErrMetadataNotAvailable } return data.ElectricalConnectionDescriptionData, nil @@ -81,14 +82,14 @@ func (e *ElectricalConnection) GetDescriptionForMeasurementId(measurementId mode return &item, nil } - return nil, ErrMetadataNotAvailable + return nil, api.ErrMetadataNotAvailable } // return parameter descriptions for all Electrical Connections func (e *ElectricalConnection) GetParameterDescriptions() ([]model.ElectricalConnectionParameterDescriptionDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionParameterDescriptionListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionParameterDescriptionListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.ElectricalConnectionParameterDescriptionData, nil @@ -109,7 +110,7 @@ func (e *ElectricalConnection) GetParameterDescriptionForScopeType(scope model.S return &element, nil } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // return parameter description for a specific parameterId @@ -127,7 +128,7 @@ func (e *ElectricalConnection) GetParameterDescriptionForParameterId(parameterId return &element, nil } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // return parameter description for a specific measurementId @@ -145,7 +146,7 @@ func (e *ElectricalConnection) GetParameterDescriptionForMeasurementId(measureme return &element, nil } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // return parameter description for a specific measurementId @@ -163,14 +164,14 @@ func (e *ElectricalConnection) GetParameterDescriptionForMeasuredPhase(phase mod return &element, nil } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // return permitted values for all Electrical Connections func (e *ElectricalConnection) GetPermittedValueSets() ([]model.ElectricalConnectionPermittedValueSetDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionPermittedValueSetListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionPermittedValueSetListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.ElectricalConnectionPermittedValueSetData, nil @@ -191,7 +192,7 @@ func (e *ElectricalConnection) GetPermittedValueSetForParameterId(parameterId mo return &element, nil } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // return permitted valueset for a provided measuremnetId @@ -214,7 +215,7 @@ func (e *ElectricalConnection) GetPermittedValueSetForMeasurementId(measurementI return &element, nil } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // returns minimum, maximum, default/pause limit values @@ -291,7 +292,7 @@ func (e *ElectricalConnection) AdjustValueToBeWithinPermittedValuesForParameter( func (e *ElectricalConnection) GetCharacteristics() ([]model.ElectricalConnectionCharacteristicDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType](e.featureRemote, model.FunctionTypeElectricalConnectionCharacteristicListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.ElectricalConnectionCharacteristicListData, nil @@ -304,7 +305,7 @@ func (e *ElectricalConnection) GetCharacteristicForContextType( ) (*model.ElectricalConnectionCharacteristicDataType, error) { data, err := e.GetCharacteristics() if err != nil || data == nil || len(data) == 0 { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } for _, item := range data { @@ -317,5 +318,5 @@ func (e *ElectricalConnection) GetCharacteristicForContextType( } } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } diff --git a/features/feature.go b/features/feature.go index 1ec4056c..8715a0a5 100644 --- a/features/feature.go +++ b/features/feature.go @@ -3,7 +3,8 @@ package features import ( "errors" - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -13,19 +14,22 @@ type Feature struct { localRole model.RoleType remoteRole model.RoleType - spineLocalDevice api.DeviceLocalInterface - localEntity api.EntityLocalInterface + spineLocalDevice spineapi.DeviceLocalInterface + localEntity spineapi.EntityLocalInterface - featureLocal api.FeatureLocalInterface - featureRemote api.FeatureRemoteInterface + featureLocal spineapi.FeatureLocalInterface + featureRemote spineapi.FeatureRemoteInterface - remoteDevice api.DeviceRemoteInterface - remoteEntity api.EntityRemoteInterface + remoteDevice spineapi.DeviceRemoteInterface + remoteEntity spineapi.EntityRemoteInterface } var _ FeatureInterface = (*Feature)(nil) -func NewFeature(featureType model.FeatureTypeType, localEntity api.EntityLocalInterface, remoteEntity api.EntityRemoteInterface) (*Feature, error) { +func NewFeature( + featureType model.FeatureTypeType, + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*Feature, error) { if localEntity == nil { return nil, errors.New("local entity is nil") } @@ -106,7 +110,9 @@ func (f *Feature) Unbind() (*model.MsgCounterType, error) { } // add a callback function to be invoked once a result to a msgCounter came in -func (f *Feature) AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) { +func (f *Feature) AddResultCallback( + msgCounterReference model.MsgCounterType, + function func(msg spineapi.ResultMessage)) { f.featureLocal.AddResultCallback(msgCounterReference, function) } @@ -116,16 +122,16 @@ func (f *Feature) AddResultCallback(msgCounterReference model.MsgCounterType, fu // both should use the proper data types for the used function func (f *Feature) requestData(function model.FunctionType, selectors any, elements any) (*model.MsgCounterType, error) { if f.featureRemote == nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } fTypes := f.featureRemote.Operations() if _, exists := fTypes[function]; !exists { - return nil, ErrFunctionNotSupported + return nil, api.ErrFunctionNotSupported } if !fTypes[function].Read() { - return nil, ErrOperationOnFunctionNotSupported + return nil, api.ErrOperationOnFunctionNotSupported } msgCounter, fErr := f.featureLocal.RequestRemoteData(function, selectors, elements, f.featureRemote) @@ -137,7 +143,10 @@ func (f *Feature) requestData(function model.FunctionType, selectors any, elemen } // internal helper method for getting local and remote feature for a given featureType and a given remoteDevice -func (f *Feature) getLocalAndRemoteFeatures() (api.FeatureLocalInterface, api.FeatureRemoteInterface, error) { +func (f *Feature) getLocalAndRemoteFeatures() ( + spineapi.FeatureLocalInterface, + spineapi.FeatureRemoteInterface, + error) { featureLocal := f.localEntity.FeatureOfTypeAndRole(f.featureType, f.localRole) if featureLocal == nil { featureLocal = f.localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeGeneric, f.localRole) diff --git a/features/identification.go b/features/identification.go index a2c35d75..4d69c325 100644 --- a/features/identification.go +++ b/features/identification.go @@ -1,7 +1,8 @@ package features import ( - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -15,8 +16,8 @@ type Identification struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewIdentification( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*Identification, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*Identification, error) { feature, err := NewFeature(model.FeatureTypeTypeIdentification, localEntity, remoteEntity) if err != nil { return nil, err @@ -38,7 +39,7 @@ func (i *Identification) RequestValues() (*model.MsgCounterType, error) { func (i *Identification) GetValues() ([]model.IdentificationDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.IdentificationListDataType](i.featureRemote, model.FunctionTypeIdentificationListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.IdentificationData, nil diff --git a/features/incentivetable.go b/features/incentivetable.go index e9a45ca5..9e0d7eb7 100644 --- a/features/incentivetable.go +++ b/features/incentivetable.go @@ -1,7 +1,8 @@ package features import ( - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -15,8 +16,8 @@ type IncentiveTable struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewIncentiveTable( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*IncentiveTable, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*IncentiveTable, error) { feature, err := NewFeature(model.FeatureTypeTypeIncentiveTable, localEntity, remoteEntity) if err != nil { return nil, err @@ -48,7 +49,7 @@ func (i *IncentiveTable) RequestValues() (*model.MsgCounterType, error) { // returns an error if this failed func (i *IncentiveTable) WriteValues(data []model.IncentiveTableType) (*model.MsgCounterType, error) { if len(data) == 0 { - return nil, ErrMissingData + return nil, api.ErrMissingData } cmd := model.CmdType{ @@ -64,7 +65,7 @@ func (i *IncentiveTable) WriteValues(data []model.IncentiveTableType) (*model.Ms func (i *IncentiveTable) GetValues() ([]model.IncentiveTableType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.IncentiveTableDataType](i.featureRemote, model.FunctionTypeIncentiveTableData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.IncentiveTable, nil @@ -74,7 +75,7 @@ func (i *IncentiveTable) GetValues() ([]model.IncentiveTableType, error) { // returns an error if this failed func (i *IncentiveTable) WriteDescriptions(data []model.IncentiveTableDescriptionType) (*model.MsgCounterType, error) { if len(data) == 0 { - return nil, ErrMissingData + return nil, api.ErrMissingData } cmd := model.CmdType{ @@ -90,7 +91,7 @@ func (i *IncentiveTable) WriteDescriptions(data []model.IncentiveTableDescriptio func (i *IncentiveTable) GetDescriptions() ([]model.IncentiveTableDescriptionType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.IncentiveTableDescriptionDataType](i.featureRemote, model.FunctionTypeIncentiveTableDescriptionData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.IncentiveTableDescription, nil @@ -111,7 +112,7 @@ func (i *IncentiveTable) GetDescriptionsForScope(scope model.ScopeTypeType) ([]m } if len(result) == 0 { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return result, nil @@ -121,7 +122,7 @@ func (i *IncentiveTable) GetDescriptionsForScope(scope model.ScopeTypeType) ([]m func (i *IncentiveTable) GetConstraints() ([]model.IncentiveTableConstraintsType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.IncentiveTableConstraintsDataType](i.featureRemote, model.FunctionTypeIncentiveTableConstraintsData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.IncentiveTableConstraints, nil diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 46e25c73..a2cae8fc 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -1,7 +1,8 @@ package features import ( - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -15,8 +16,8 @@ type LoadControl struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewLoadControl( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*LoadControl, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*LoadControl, error) { feature, err := NewFeature(model.FeatureTypeTypeLoadControl, localEntity, remoteEntity) if err != nil { return nil, err @@ -49,7 +50,7 @@ func (l *LoadControl) RequestLimitValues() (*model.MsgCounterType, error) { func (l *LoadControl) GetLimitDescriptions() ([]model.LoadControlLimitDescriptionDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType](l.featureRemote, model.FunctionTypeLoadControlLimitDescriptionListData) if err != nil { - return nil, ErrMetadataNotAvailable + return nil, api.ErrMetadataNotAvailable } return data.LoadControlLimitDescriptionData, nil @@ -72,7 +73,7 @@ func (l *LoadControl) GetLimitDescriptionsForCategory(category model.LoadControl } if len(result) == 0 { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return result, nil @@ -108,7 +109,7 @@ func (l *LoadControl) GetLimitDescriptionsForCategoryTypeDirectionScope( } if len(result) == 0 { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return result, nil @@ -131,7 +132,7 @@ func (l *LoadControl) GetLimitDescriptionsForMeasurementId(measurementId model.M } if len(result) == 0 { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return result, nil @@ -141,7 +142,7 @@ func (l *LoadControl) GetLimitDescriptionsForMeasurementId(measurementId model.M // returns an error if this failed func (l *LoadControl) WriteLimitValues(data []model.LoadControlLimitDataType) (*model.MsgCounterType, error) { if len(data) == 0 { - return nil, ErrMissingData + return nil, api.ErrMissingData } cmd := model.CmdType{ @@ -157,7 +158,7 @@ func (l *LoadControl) WriteLimitValues(data []model.LoadControlLimitDataType) (* func (l *LoadControl) GetLimitValues() ([]model.LoadControlLimitDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.LoadControlLimitListDataType](l.featureRemote, model.FunctionTypeLoadControlLimitListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.LoadControlLimitData, nil @@ -176,5 +177,5 @@ func (l *LoadControl) GetLimitValueForLimitId(limitId model.LoadControlLimitIdTy } } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } diff --git a/features/measurement.go b/features/measurement.go index eaa8102f..5cba18ea 100644 --- a/features/measurement.go +++ b/features/measurement.go @@ -1,7 +1,8 @@ package features import ( - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -15,8 +16,8 @@ type Measurement struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewMeasurement( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*Measurement, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*Measurement, error) { feature, err := NewFeature(model.FeatureTypeTypeMeasurement, localEntity, remoteEntity) if err != nil { return nil, err @@ -48,7 +49,7 @@ func (m *Measurement) RequestValues() (*model.MsgCounterType, error) { func (m *Measurement) GetDescriptions() ([]model.MeasurementDescriptionDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.MeasurementDescriptionListDataType](m.featureRemote, model.FunctionTypeMeasurementDescriptionListData) if err != nil { - return nil, ErrMetadataNotAvailable + return nil, api.ErrMetadataNotAvailable } return data.MeasurementDescriptionData, nil @@ -69,7 +70,7 @@ func (m *Measurement) GetDescriptionsForScope(scope model.ScopeTypeType) ([]mode } if len(result) == 0 { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return result, nil @@ -91,14 +92,14 @@ func (m *Measurement) GetDescriptionForMeasurementId(measurementId model.Measure return &item, nil } - return nil, ErrMetadataNotAvailable + return nil, api.ErrMetadataNotAvailable } // return current values for measurements func (m *Measurement) GetValues() ([]model.MeasurementDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.MeasurementListDataType](m.featureRemote, model.FunctionTypeMeasurementListData) if err != nil { - return nil, ErrMetadataNotAvailable + return nil, api.ErrMetadataNotAvailable } return data.MeasurementData, nil @@ -123,7 +124,7 @@ func (m *Measurement) GetValueForMeasurementId(id model.MeasurementIdType) (floa } } - return 0, ErrDataNotAvailable + return 0, api.ErrDataNotAvailable } // return current values of a defined measurementType, commodityType and scopeType @@ -153,7 +154,7 @@ func (m *Measurement) GetValuesForTypeCommodityScope(measurement model.Measureme } if len(resultSet) == 0 { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return resultSet, nil @@ -163,7 +164,7 @@ func (m *Measurement) GetValuesForTypeCommodityScope(measurement model.Measureme func (m *Measurement) GetConstraints() ([]model.MeasurementConstraintsDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.MeasurementConstraintsListDataType](m.featureRemote, model.FunctionTypeMeasurementConstraintsListData) if err != nil { - return nil, ErrMetadataNotAvailable + return nil, api.ErrMetadataNotAvailable } return data.MeasurementConstraintsData, nil diff --git a/features/timeseries.go b/features/timeseries.go index 8d2a0205..b5c9a6fc 100644 --- a/features/timeseries.go +++ b/features/timeseries.go @@ -1,7 +1,8 @@ package features import ( - "github.com/enbility/spine-go/api" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) @@ -15,8 +16,8 @@ type TimeSeries struct { // - The feature on the local entity has to be of role client // - The feature on the remote entity has to be of role server func NewTimeSeries( - localEntity api.EntityLocalInterface, - remoteEntity api.EntityRemoteInterface) (*TimeSeries, error) { + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*TimeSeries, error) { feature, err := NewFeature(model.FeatureTypeTypeTimeSeries, localEntity, remoteEntity) if err != nil { return nil, err @@ -48,7 +49,7 @@ func (t *TimeSeries) RequestValues() (*model.MsgCounterType, error) { // returns an error if this failed func (t *TimeSeries) WriteValues(data []model.TimeSeriesDataType) (*model.MsgCounterType, error) { if len(data) == 0 { - return nil, ErrMissingData + return nil, api.ErrMissingData } cmd := model.CmdType{ @@ -64,7 +65,7 @@ func (t *TimeSeries) WriteValues(data []model.TimeSeriesDataType) (*model.MsgCou func (t *TimeSeries) GetValues() ([]model.TimeSeriesDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.TimeSeriesListDataType](t.featureRemote, model.FunctionTypeTimeSeriesListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.TimeSeriesData, nil @@ -95,14 +96,14 @@ func (t *TimeSeries) GetValueForType(timeSeriesType model.TimeSeriesTypeType) (* return &item, nil } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // return list of descriptions func (t *TimeSeries) GetDescriptions() ([]model.TimeSeriesDescriptionDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.TimeSeriesDescriptionListDataType](t.featureRemote, model.FunctionTypeTimeSeriesDescriptionListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.TimeSeriesDescriptionData, nil @@ -120,7 +121,7 @@ func (t *TimeSeries) GetDescriptionForId(id model.TimeSeriesIdType) (*model.Time } } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } func (t *TimeSeries) GetDescriptionForType(timeSeriesType model.TimeSeriesTypeType) (*model.TimeSeriesDescriptionDataType, error) { @@ -135,14 +136,14 @@ func (t *TimeSeries) GetDescriptionForType(timeSeriesType model.TimeSeriesTypeTy } } - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } // return current constraints for Time Series func (t *TimeSeries) GetConstraints() ([]model.TimeSeriesConstraintsDataType, error) { data, err := spine.RemoteFeatureDataCopyOfType[*model.TimeSeriesConstraintsListDataType](t.featureRemote, model.FunctionTypeTimeSeriesConstraintsListData) if err != nil { - return nil, ErrDataNotAvailable + return nil, api.ErrDataNotAvailable } return data.TimeSeriesConstraintsData, nil From 1494dc3a8a7c3ef3bbe0b26f3c4966fd2a1f958b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 27 Feb 2024 19:04:45 +0100 Subject: [PATCH 211/240] Update ship --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 35132ef6..9b3daee8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 + github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 0824603a..9f89f2fb 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= -github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae h1:KOowUYPF7qmNvKLt81SPOslrOKyph2LEV8EyZ8xjxd8= +github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 h1:/oXzUGRXtOlQRflZD6nMd3aFOvl2n78u6G1v0IzGz8U= github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= From 25e3b22545adb88a74cd2df312d56fdcfeb19895 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 28 Feb 2024 13:31:51 +0100 Subject: [PATCH 212/240] Update ship & spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9b3daee8..0d250128 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae - github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 + github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9 + github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 9f89f2fb..fa88f713 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae h1:KOowUYPF7qmNvKLt81SPOslrOKyph2LEV8EyZ8xjxd8= -github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 h1:/oXzUGRXtOlQRflZD6nMd3aFOvl2n78u6G1v0IzGz8U= -github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9 h1:+TxssP/V5+k3HBJl+huN/oZQ3D7R3TUU4S1GP+jkUuM= +github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3 h1:Mz+/gSmp8QkZqR6hOyYh1zRUAL8vAT63bxSbYG4m+YM= +github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 4437d35c805b9b8d1d9155d8b7bc7d0e37184937 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 28 Feb 2024 13:34:08 +0100 Subject: [PATCH 213/240] Update API, more documentation Remove `AllowWaitingForTrust` from the `ServiceReaderInterface` and instead add a public method `UserIsAbleToApproveOrCancelPairingRequests` which can be used actively by the service implementation --- api/api.go | 26 ++++++++++++++---- mocks/ServiceInterface.go | 35 +++++++++++++++++++++++- mocks/ServiceReaderInterface.go | 48 +-------------------------------- service/service.go | 19 +++++++++++++ service/service_hub.go | 5 +++- service/service_test.go | 12 +++++++-- 6 files changed, 89 insertions(+), 56 deletions(-) diff --git a/api/api.go b/api/api.go index 0cf17a4e..4ecfe914 100644 --- a/api/api.go +++ b/api/api.go @@ -41,20 +41,39 @@ type ServiceInterface interface { // Provide the current pairing state for a SKI PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail - // Returns the Service detail of a given remote SKI + // Returns the Service detail of a remote SKI RemoteServiceForSKI(ski string) *shipapi.ServiceDetails // Sets the SKI as being paired or not + // + // This should be called before `Start` for any SKI that has been + // paired in a previous session RegisterRemoteSKI(ski string, enable bool) - // Disconnect a connection to an SKI + // Disconnect from a connected remote SKI DisconnectSKI(ski string, reason string) // Triggers the pairing process for a SKI + // + // This should be called while the service is running and the end + // user selected to initiate the pairing process with a device + // or wants to approve an incoming pairing request InitiateOrApprovePairingWithSKI(ski string) // Cancels the pairing process for a SKI + // + // This should be called while the service is running and the end + // user wants to cancel/disallow an incoming pairing request CancelPairingWithSKI(ski string) + + // Define wether the user is able to react to an incoming pairing request + // + // Call this with `true` e.g. if the user is currently using a web interface + // where an incoming request can be accepted or denied + // + // Default is set to false, meaning every incoming pairing request will be + // automatically denied + UserIsAbleToApproveOrCancelPairingRequests(allow bool) } // interface for receiving data for specific events from Service @@ -82,7 +101,4 @@ type ServiceReaderInterface interface { // This is called whenever the state changes and can be used to // provide user information for the pairing/connection process ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) - - // return if the user is still able to trust the connection - AllowWaitingForTrust(ski string) bool } diff --git a/mocks/ServiceInterface.go b/mocks/ServiceInterface.go index 9d27634b..79b5c8a5 100644 --- a/mocks/ServiceInterface.go +++ b/mocks/ServiceInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.3. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -539,6 +539,39 @@ func (_c *ServiceInterface_Start_Call) RunAndReturn(run func()) *ServiceInterfac return _c } +// UserIsAbleToApproveOrCancelPairingRequests provides a mock function with given fields: allow +func (_m *ServiceInterface) UserIsAbleToApproveOrCancelPairingRequests(allow bool) { + _m.Called(allow) +} + +// ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UserIsAbleToApproveOrCancelPairingRequests' +type ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call struct { + *mock.Call +} + +// UserIsAbleToApproveOrCancelPairingRequests is a helper method to define mock.On call +// - allow bool +func (_e *ServiceInterface_Expecter) UserIsAbleToApproveOrCancelPairingRequests(allow interface{}) *ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call { + return &ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call{Call: _e.mock.On("UserIsAbleToApproveOrCancelPairingRequests", allow)} +} + +func (_c *ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call) Run(run func(allow bool)) *ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call) Return() *ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call) RunAndReturn(run func(bool)) *ServiceInterface_UserIsAbleToApproveOrCancelPairingRequests_Call { + _c.Call.Return(run) + return _c +} + // NewServiceInterface creates a new instance of ServiceInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewServiceInterface(t interface { diff --git a/mocks/ServiceReaderInterface.go b/mocks/ServiceReaderInterface.go index da8ee045..1b1d76da 100644 --- a/mocks/ServiceReaderInterface.go +++ b/mocks/ServiceReaderInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.3. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -22,52 +22,6 @@ func (_m *ServiceReaderInterface) EXPECT() *ServiceReaderInterface_Expecter { return &ServiceReaderInterface_Expecter{mock: &_m.Mock} } -// AllowWaitingForTrust provides a mock function with given fields: ski -func (_m *ServiceReaderInterface) AllowWaitingForTrust(ski string) bool { - ret := _m.Called(ski) - - if len(ret) == 0 { - panic("no return value specified for AllowWaitingForTrust") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(string) bool); ok { - r0 = rf(ski) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// ServiceReaderInterface_AllowWaitingForTrust_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AllowWaitingForTrust' -type ServiceReaderInterface_AllowWaitingForTrust_Call struct { - *mock.Call -} - -// AllowWaitingForTrust is a helper method to define mock.On call -// - ski string -func (_e *ServiceReaderInterface_Expecter) AllowWaitingForTrust(ski interface{}) *ServiceReaderInterface_AllowWaitingForTrust_Call { - return &ServiceReaderInterface_AllowWaitingForTrust_Call{Call: _e.mock.On("AllowWaitingForTrust", ski)} -} - -func (_c *ServiceReaderInterface_AllowWaitingForTrust_Call) Run(run func(ski string)) *ServiceReaderInterface_AllowWaitingForTrust_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ServiceReaderInterface_AllowWaitingForTrust_Call) Return(_a0 bool) *ServiceReaderInterface_AllowWaitingForTrust_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *ServiceReaderInterface_AllowWaitingForTrust_Call) RunAndReturn(run func(string) bool) *ServiceReaderInterface_AllowWaitingForTrust_Call { - _c.Call.Return(run) - return _c -} - // RemoteSKIConnected provides a mock function with given fields: service, ski func (_m *ServiceReaderInterface) RemoteSKIConnected(service api.ServiceInterface, ski string) { _m.Called(service, ski) diff --git a/service/service.go b/service/service.go index 78d6fa51..1bf95831 100644 --- a/service/service.go +++ b/service/service.go @@ -33,7 +33,12 @@ type Service struct { serviceHandler api.ServiceReaderInterface + // defines wether a user interaction to accept pairing is possible + isPairingPossible bool + startOnce sync.Once + + mux sync.Mutex } // creates a new EEBUS service @@ -195,3 +200,17 @@ func (s *Service) InitiateOrApprovePairingWithSKI(ski string) { func (s *Service) CancelPairingWithSKI(ski string) { s.connectionsHub.CancelPairingWithSKI(ski) } + +// Define wether the user is able to react to an incoming pairing request +// +// Call this with `true` e.g. if the user is currently using a web interface +// where an incoming request can be accepted or denied +// +// Default is set to false, meaning every incoming pairing request will be +// automatically denied +func (s *Service) UserIsAbleToApproveOrCancelPairingRequests(allow bool) { + s.mux.Lock() + defer s.mux.Unlock() + + s.isPairingPossible = allow +} diff --git a/service/service_hub.go b/service/service_hub.go index cd285719..27230d75 100644 --- a/service/service_hub.go +++ b/service/service_hub.go @@ -46,5 +46,8 @@ func (s *Service) ServicePairingDetailUpdate(ski string, detail *shipapi.Connect // return if the user is still able to trust the connection func (s *Service) AllowWaitingForTrust(ski string) bool { - return s.serviceHandler.AllowWaitingForTrust(ski) + s.mux.Lock() + defer s.mux.Unlock() + + return s.isPairingPossible } diff --git a/service/service_test.go b/service/service_test.go index 18c7e084..1aca3fd7 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -47,9 +47,11 @@ func (s *ServiceSuite) BeforeTest(suiteName, testName string) { s.localDevice = spinemocks.NewDeviceLocalInterface(s.T()) certificate := tls.Certificate{} - s.config, _ = api.NewConfiguration( + var err error + s.config, err = api.NewConfiguration( "vendor", "brand", "model", "serial", model.DeviceTypeTypeEnergyManagementSystem, []model.EntityTypeType{model.EntityTypeTypeCEM}, 4729, certificate, 230.0, time.Second*4) + assert.Nil(s.T(), nil, err) s.sut = NewService(s.config, s.serviceReader) } @@ -81,10 +83,15 @@ func (s *ServiceSuite) Test_EEBUSHandler() { detail := &shipapi.ConnectionStateDetail{} s.sut.ServicePairingDetailUpdate(testSki, detail) - s.serviceReader.EXPECT().AllowWaitingForTrust(mock.Anything).Return(true) + s.sut.UserIsAbleToApproveOrCancelPairingRequests(true) result := s.sut.AllowWaitingForTrust(testSki) assert.Equal(s.T(), true, result) + conf := s.sut.Configuration() + assert.Equal(s.T(), s.sut.configuration, conf) + + lService := s.sut.LocalService() + assert.Equal(s.T(), s.sut.localService, lService) } func (s *ServiceSuite) Test_ConnectionsHub() { @@ -114,6 +121,7 @@ func (s *ServiceSuite) Test_ConnectionsHub() { s.conHub.EXPECT().DisconnectSKI(mock.Anything, mock.Anything).Return() s.sut.DisconnectSKI(testSki, "reason") + } func (s *ServiceSuite) Test_SetLogging() { From f618388b91ab205e24076dda584dfd9fa09d9bd2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 7 Mar 2024 14:32:49 +0100 Subject: [PATCH 214/240] Update SHIP and SPINE --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0d250128..48db61a0 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9 - github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3 + github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 + github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index fa88f713..6571a286 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9 h1:+TxssP/V5+k3HBJl+huN/oZQ3D7R3TUU4S1GP+jkUuM= -github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3 h1:Mz+/gSmp8QkZqR6hOyYh1zRUAL8vAT63bxSbYG4m+YM= -github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= +github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1 h1:S6WIvMkyboVoZv1FSMABJ2mTZNjvKzbje9xL1JVUxsM= +github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 74134889a2ffde160b583cb80470d8fe63a1b5b6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 7 Mar 2024 21:03:29 +0100 Subject: [PATCH 215/240] Fix loadcontrol function naming --- features/loadcontrol.go | 2 +- features/loadcontrol_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/loadcontrol.go b/features/loadcontrol.go index a2cae8fc..b1dcb4f8 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -81,7 +81,7 @@ func (l *LoadControl) GetLimitDescriptionsForCategory(category model.LoadControl // returns the load control limit descriptions of a provided type, direction and scope // returns an error if no description data for the category is available -func (l *LoadControl) GetLimitDescriptionsForCategoryTypeDirectionScope( +func (l *LoadControl) GetLimitDescriptionsForTypeCategoryDirectionScope( limitType model.LoadControlLimitTypeType, limitCategory model.LoadControlCategoryType, limitDirection model.EnergyDirectionType, diff --git a/features/loadcontrol_test.go b/features/loadcontrol_test.go index e0a77ec0..fa6b6928 100644 --- a/features/loadcontrol_test.go +++ b/features/loadcontrol_test.go @@ -101,7 +101,7 @@ func (s *LoadControlSuite) Test_GetLimitDescriptionsForCategory() { } func (s *LoadControlSuite) Test_GetLimitDescriptionsForTypeDirectionScope() { - data, err := s.loadControl.GetLimitDescriptionsForCategoryTypeDirectionScope( + data, err := s.loadControl.GetLimitDescriptionsForTypeCategoryDirectionScope( model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, model.LoadControlCategoryTypeObligation, model.EnergyDirectionTypeConsume, @@ -111,7 +111,7 @@ func (s *LoadControlSuite) Test_GetLimitDescriptionsForTypeDirectionScope() { s.addDescription() - data, err = s.loadControl.GetLimitDescriptionsForCategoryTypeDirectionScope( + data, err = s.loadControl.GetLimitDescriptionsForTypeCategoryDirectionScope( model.LoadControlLimitTypeTypeMaxValueLimit, model.LoadControlCategoryTypeObligation, model.EnergyDirectionTypeConsume, @@ -119,7 +119,7 @@ func (s *LoadControlSuite) Test_GetLimitDescriptionsForTypeDirectionScope() { assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.loadControl.GetLimitDescriptionsForCategoryTypeDirectionScope( + data, err = s.loadControl.GetLimitDescriptionsForTypeCategoryDirectionScope( model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, model.LoadControlCategoryTypeObligation, model.EnergyDirectionTypeConsume, From 990af977c3fff4b23357be8cc98008cd42c288b9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 8 Mar 2024 15:57:25 +0100 Subject: [PATCH 216/240] Update SPINE --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 48db61a0..41a96058 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1 + github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 6571a286..10278725 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1 h1:S6WIvMkyboVoZv1FSMABJ2mTZNjvKzbje9xL1JVUxsM= -github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10 h1:d+KO3JsZsqaixFEr0z2pSQWNKAGrapQ+w9TH+/6RbHA= +github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From f56210e7c21d58fb248530240b0d4f03e7a37e4e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 8 Mar 2024 17:35:32 +0100 Subject: [PATCH 217/240] Add DeviceConofiguration write & all write partial --- features/deviceconfiguration.go | 19 +++++++++++++++++++ features/deviceconfiguration_test.go | 23 +++++++++++++++++++++++ features/loadcontrol.go | 3 +++ 3 files changed, 45 insertions(+) diff --git a/features/deviceconfiguration.go b/features/deviceconfiguration.go index 2842f7cf..8275ba62 100644 --- a/features/deviceconfiguration.go +++ b/features/deviceconfiguration.go @@ -2,6 +2,7 @@ package features import ( "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -95,6 +96,24 @@ func (d *DeviceConfiguration) GetKeyValues() ([]model.DeviceConfigurationKeyValu return data.DeviceConfigurationKeyValueData, nil } +// write key values +// returns an error if this failed +func (d *DeviceConfiguration) WriteKeyValues(data []model.DeviceConfigurationKeyValueDataType) (*model.MsgCounterType, error) { + if len(data) == 0 { + return nil, api.ErrMissingData + } + + cmd := model.CmdType{ + Function: util.Ptr(model.FunctionTypeDeviceConfigurationKeyValueListData), + Filter: []model.FilterType{*model.NewFilterTypePartial()}, + DeviceConfigurationKeyValueListData: &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: data, + }, + } + + return d.remoteDevice.Sender().Write(d.featureLocal.Address(), d.featureRemote.Address(), cmd) +} + // return a pointer value for a given key and value type func (d *DeviceConfiguration) GetKeyValueForKeyName(keyname model.DeviceConfigurationKeyNameType, valueType model.DeviceConfigurationKeyValueTypeType) (any, error) { values, err := d.GetKeyValues() diff --git a/features/deviceconfiguration_test.go b/features/deviceconfiguration_test.go index 45af473c..7def2cb4 100644 --- a/features/deviceconfiguration_test.go +++ b/features/deviceconfiguration_test.go @@ -159,6 +159,29 @@ func (s *DeviceConfigurationSuite) Test_GetValues() { assert.NotNil(s.T(), data) } +func (s *DeviceConfigurationSuite) Test_WriteValues() { + counter, err := s.deviceConfiguration.WriteKeyValues(nil) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), counter) + + data := []model.DeviceConfigurationKeyValueDataType{} + counter, err = s.deviceConfiguration.WriteKeyValues(data) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), counter) + + data = []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(10), + }, + }, + } + counter, err = s.deviceConfiguration.WriteKeyValues(data) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) +} + // helper func (s *DeviceConfigurationSuite) addDescription() { diff --git a/features/loadcontrol.go b/features/loadcontrol.go index b1dcb4f8..0d51cf8e 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -2,6 +2,7 @@ package features import ( "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -146,6 +147,8 @@ func (l *LoadControl) WriteLimitValues(data []model.LoadControlLimitDataType) (* } cmd := model.CmdType{ + Function: util.Ptr(model.FunctionTypeLoadControlLimitListData), + Filter: []model.FilterType{*model.NewFilterTypePartial()}, LoadControlLimitListData: &model.LoadControlLimitListDataType{ LoadControlLimitData: data, }, From 90c4d4f160f0c7e543058f62f70642c112a93cb0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 9 Mar 2024 19:26:57 +0100 Subject: [PATCH 218/240] Update SPINE --- features/electricalconnection_test.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 3c99f407..91b1bf0a 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -378,7 +378,7 @@ func (s *ElectricalConnectionSuite) addCharacteristics() { { ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - CharacteristicId: util.Ptr(model.ElectricalConnectionCharaceteristicIdType(0)), + CharacteristicId: util.Ptr(model.ElectricalConnectionCharacteristicIdType(0)), CharacteristicContext: util.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), CharacteristicType: util.Ptr(model.ElectricalConnectionCharacteristicTypeTypeEnergyCapacityNominalMax), Value: model.NewScaledNumberType(98), diff --git a/go.mod b/go.mod index 41a96058..2033f55a 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10 + github.com/enbility/spine-go v0.0.0-20240309182420-5559e12ef2f0 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 10278725..0f5bcd71 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10 h1:d+KO3JsZsqaixFEr0z2pSQWNKAGrapQ+w9TH+/6RbHA= -github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240309182420-5559e12ef2f0 h1:pYQHPr1n3oKKvSlzfgycWkf+/GB9fLFm1RFSxRFLw8w= +github.com/enbility/spine-go v0.0.0-20240309182420-5559e12ef2f0/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From f92663e820081dfbd9505625568c3090a38e9e9b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 11 Mar 2024 16:02:32 +0100 Subject: [PATCH 219/240] Update SPINE --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2033f55a..24a09e96 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240309182420-5559e12ef2f0 + github.com/enbility/spine-go v0.0.0-20240311135232-94aaea2582e6 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 0f5bcd71..90e81cf3 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240309182420-5559e12ef2f0 h1:pYQHPr1n3oKKvSlzfgycWkf+/GB9fLFm1RFSxRFLw8w= -github.com/enbility/spine-go v0.0.0-20240309182420-5559e12ef2f0/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240311135232-94aaea2582e6 h1:Nr36e5KOSg1iNierwVmQjdrSNBcUPlA60iDbEGFWWxA= +github.com/enbility/spine-go v0.0.0-20240311135232-94aaea2582e6/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 084d7920b70a234800eaa1e80f3920672cf0b5e7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 11 Mar 2024 16:03:25 +0100 Subject: [PATCH 220/240] Update to latest SPINE changes --- features/api.go | 4 ++-- features/feature.go | 6 +++--- features/feature_test.go | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/features/api.go b/features/api.go index 9d3f0149..b14ac0cb 100644 --- a/features/api.go +++ b/features/api.go @@ -19,6 +19,6 @@ type FeatureInterface interface { // bind to the feature of the entity Bind() (*model.MsgCounterType, error) - // add a callback function to be invoked once a result to a msgCounter came in - AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResultMessage)) + // add a callback function to be invoked once a result or reply message for a msgCounter came in + AddResponseCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResponseMessage)) error } diff --git a/features/feature.go b/features/feature.go index 8715a0a5..1e9804db 100644 --- a/features/feature.go +++ b/features/feature.go @@ -110,10 +110,10 @@ func (f *Feature) Unbind() (*model.MsgCounterType, error) { } // add a callback function to be invoked once a result to a msgCounter came in -func (f *Feature) AddResultCallback( +func (f *Feature) AddResponseCallback( msgCounterReference model.MsgCounterType, - function func(msg spineapi.ResultMessage)) { - f.featureLocal.AddResultCallback(msgCounterReference, function) + function func(msg spineapi.ResponseMessage)) error { + return f.featureLocal.AddResponseCallback(msgCounterReference, function) } // helper method which adds checking if the feature is available and the operation is allowed diff --git a/features/feature_test.go b/features/feature_test.go index aef2d65b..516f6c52 100644 --- a/features/feature_test.go +++ b/features/feature_test.go @@ -120,5 +120,6 @@ func (s *FeatureSuite) Test_Binding() { } func (s *FeatureSuite) Test_ResultCallback() { - s.testFeature.AddResultCallback(10, func(msg spineapi.ResultMessage) {}) + err := s.testFeature.AddResponseCallback(10, func(msg spineapi.ResponseMessage) {}) + assert.Nil(s.T(), err) } From efc136038ba40c15cfee6353fc8b610d4c51b75b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 11 Mar 2024 16:03:42 +0100 Subject: [PATCH 221/240] Add mocks of features package --- api/.mockery.yaml => .mockery.yaml | 3 +- mocks/FeatureInterface.go | 289 +++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+), 1 deletion(-) rename api/.mockery.yaml => .mockery.yaml (69%) create mode 100644 mocks/FeatureInterface.go diff --git a/api/.mockery.yaml b/.mockery.yaml similarity index 69% rename from api/.mockery.yaml rename to .mockery.yaml index 01e7959b..d4b533e7 100644 --- a/api/.mockery.yaml +++ b/.mockery.yaml @@ -1,9 +1,10 @@ with-expecter: True inpackage: false -dir: ../mocks/{{ replaceAll .InterfaceDirRelative "internal" "internal_" }} +dir: "{{ .InterfaceDir }}/../mocks" mockname: "{{.InterfaceName}}" outpkg: "mocks" filename: "{{.InterfaceName}}.go" all: True packages: github.com/enbility/eebus-go/api: + github.com/enbility/eebus-go/features: diff --git a/mocks/FeatureInterface.go b/mocks/FeatureInterface.go new file mode 100644 index 00000000..c5a2c7ce --- /dev/null +++ b/mocks/FeatureInterface.go @@ -0,0 +1,289 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// FeatureInterface is an autogenerated mock type for the FeatureInterface type +type FeatureInterface struct { + mock.Mock +} + +type FeatureInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *FeatureInterface) EXPECT() *FeatureInterface_Expecter { + return &FeatureInterface_Expecter{mock: &_m.Mock} +} + +// AddResponseCallback provides a mock function with given fields: msgCounterReference, function +func (_m *FeatureInterface) AddResponseCallback(msgCounterReference model.MsgCounterType, function func(api.ResponseMessage)) error { + ret := _m.Called(msgCounterReference, function) + + if len(ret) == 0 { + panic("no return value specified for AddResponseCallback") + } + + var r0 error + if rf, ok := ret.Get(0).(func(model.MsgCounterType, func(api.ResponseMessage)) error); ok { + r0 = rf(msgCounterReference, function) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// FeatureInterface_AddResponseCallback_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddResponseCallback' +type FeatureInterface_AddResponseCallback_Call struct { + *mock.Call +} + +// AddResponseCallback is a helper method to define mock.On call +// - msgCounterReference model.MsgCounterType +// - function func(api.ResponseMessage) +func (_e *FeatureInterface_Expecter) AddResponseCallback(msgCounterReference interface{}, function interface{}) *FeatureInterface_AddResponseCallback_Call { + return &FeatureInterface_AddResponseCallback_Call{Call: _e.mock.On("AddResponseCallback", msgCounterReference, function)} +} + +func (_c *FeatureInterface_AddResponseCallback_Call) Run(run func(msgCounterReference model.MsgCounterType, function func(api.ResponseMessage))) *FeatureInterface_AddResponseCallback_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(model.MsgCounterType), args[1].(func(api.ResponseMessage))) + }) + return _c +} + +func (_c *FeatureInterface_AddResponseCallback_Call) Return(_a0 error) *FeatureInterface_AddResponseCallback_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *FeatureInterface_AddResponseCallback_Call) RunAndReturn(run func(model.MsgCounterType, func(api.ResponseMessage)) error) *FeatureInterface_AddResponseCallback_Call { + _c.Call.Return(run) + return _c +} + +// Bind provides a mock function with given fields: +func (_m *FeatureInterface) Bind() (*model.MsgCounterType, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Bind") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func() (*model.MsgCounterType, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *model.MsgCounterType); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FeatureInterface_Bind_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Bind' +type FeatureInterface_Bind_Call struct { + *mock.Call +} + +// Bind is a helper method to define mock.On call +func (_e *FeatureInterface_Expecter) Bind() *FeatureInterface_Bind_Call { + return &FeatureInterface_Bind_Call{Call: _e.mock.On("Bind")} +} + +func (_c *FeatureInterface_Bind_Call) Run(run func()) *FeatureInterface_Bind_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *FeatureInterface_Bind_Call) Return(_a0 *model.MsgCounterType, _a1 error) *FeatureInterface_Bind_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *FeatureInterface_Bind_Call) RunAndReturn(run func() (*model.MsgCounterType, error)) *FeatureInterface_Bind_Call { + _c.Call.Return(run) + return _c +} + +// HasBinding provides a mock function with given fields: +func (_m *FeatureInterface) HasBinding() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for HasBinding") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// FeatureInterface_HasBinding_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HasBinding' +type FeatureInterface_HasBinding_Call struct { + *mock.Call +} + +// HasBinding is a helper method to define mock.On call +func (_e *FeatureInterface_Expecter) HasBinding() *FeatureInterface_HasBinding_Call { + return &FeatureInterface_HasBinding_Call{Call: _e.mock.On("HasBinding")} +} + +func (_c *FeatureInterface_HasBinding_Call) Run(run func()) *FeatureInterface_HasBinding_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *FeatureInterface_HasBinding_Call) Return(_a0 bool) *FeatureInterface_HasBinding_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *FeatureInterface_HasBinding_Call) RunAndReturn(run func() bool) *FeatureInterface_HasBinding_Call { + _c.Call.Return(run) + return _c +} + +// HasSubscription provides a mock function with given fields: +func (_m *FeatureInterface) HasSubscription() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for HasSubscription") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// FeatureInterface_HasSubscription_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HasSubscription' +type FeatureInterface_HasSubscription_Call struct { + *mock.Call +} + +// HasSubscription is a helper method to define mock.On call +func (_e *FeatureInterface_Expecter) HasSubscription() *FeatureInterface_HasSubscription_Call { + return &FeatureInterface_HasSubscription_Call{Call: _e.mock.On("HasSubscription")} +} + +func (_c *FeatureInterface_HasSubscription_Call) Run(run func()) *FeatureInterface_HasSubscription_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *FeatureInterface_HasSubscription_Call) Return(_a0 bool) *FeatureInterface_HasSubscription_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *FeatureInterface_HasSubscription_Call) RunAndReturn(run func() bool) *FeatureInterface_HasSubscription_Call { + _c.Call.Return(run) + return _c +} + +// Subscribe provides a mock function with given fields: +func (_m *FeatureInterface) Subscribe() (*model.MsgCounterType, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func() (*model.MsgCounterType, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *model.MsgCounterType); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FeatureInterface_Subscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Subscribe' +type FeatureInterface_Subscribe_Call struct { + *mock.Call +} + +// Subscribe is a helper method to define mock.On call +func (_e *FeatureInterface_Expecter) Subscribe() *FeatureInterface_Subscribe_Call { + return &FeatureInterface_Subscribe_Call{Call: _e.mock.On("Subscribe")} +} + +func (_c *FeatureInterface_Subscribe_Call) Run(run func()) *FeatureInterface_Subscribe_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *FeatureInterface_Subscribe_Call) Return(_a0 *model.MsgCounterType, _a1 error) *FeatureInterface_Subscribe_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *FeatureInterface_Subscribe_Call) RunAndReturn(run func() (*model.MsgCounterType, error)) *FeatureInterface_Subscribe_Call { + _c.Call.Return(run) + return _c +} + +// NewFeatureInterface creates a new instance of FeatureInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFeatureInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *FeatureInterface { + mock := &FeatureInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 5a60b22b2f4cb8a7c412a00bac68c027222a25ae Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 11 Mar 2024 16:28:14 +0100 Subject: [PATCH 222/240] Update SPINE and add new result Callback API --- features/api.go | 3 +++ features/feature.go | 5 +++++ features/feature_test.go | 5 ++++- go.mod | 2 +- go.sum | 4 ++-- mocks/FeatureInterface.go | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/features/api.go b/features/api.go index b14ac0cb..edb4db43 100644 --- a/features/api.go +++ b/features/api.go @@ -21,4 +21,7 @@ type FeatureInterface interface { // add a callback function to be invoked once a result or reply message for a msgCounter came in AddResponseCallback(msgCounterReference model.MsgCounterType, function func(msg api.ResponseMessage)) error + + // add a callback function to be invoked once a result came in + AddResultCallback(function func(msg api.ResponseMessage)) } diff --git a/features/feature.go b/features/feature.go index 1e9804db..2af86fb1 100644 --- a/features/feature.go +++ b/features/feature.go @@ -116,6 +116,11 @@ func (f *Feature) AddResponseCallback( return f.featureLocal.AddResponseCallback(msgCounterReference, function) } +// add a callback function to be invoked once a result came in +func (f *Feature) AddResultCallback(function func(msg spineapi.ResponseMessage)) { + f.featureLocal.AddResultCallback(function) +} + // helper method which adds checking if the feature is available and the operation is allowed // selectors and elements are used if specific data should be requested by using // model.FilterType DataSelectors (selectors) and/or DataElements (elements) diff --git a/features/feature_test.go b/features/feature_test.go index 516f6c52..b6e57899 100644 --- a/features/feature_test.go +++ b/features/feature_test.go @@ -120,6 +120,9 @@ func (s *FeatureSuite) Test_Binding() { } func (s *FeatureSuite) Test_ResultCallback() { - err := s.testFeature.AddResponseCallback(10, func(msg spineapi.ResponseMessage) {}) + testFct := func(msg spineapi.ResponseMessage) {} + err := s.testFeature.AddResponseCallback(10, testFct) assert.Nil(s.T(), err) + + s.testFeature.AddResultCallback(testFct) } diff --git a/go.mod b/go.mod index 24a09e96..ce3b24d1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240311135232-94aaea2582e6 + github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 90e81cf3..ccdd05e0 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240311135232-94aaea2582e6 h1:Nr36e5KOSg1iNierwVmQjdrSNBcUPlA60iDbEGFWWxA= -github.com/enbility/spine-go v0.0.0-20240311135232-94aaea2582e6/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654 h1:5oHCw3s4igUttQwEpf5yRNly3GNSQtMX9V652b9VWlU= +github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/mocks/FeatureInterface.go b/mocks/FeatureInterface.go index c5a2c7ce..0a2abc75 100644 --- a/mocks/FeatureInterface.go +++ b/mocks/FeatureInterface.go @@ -70,6 +70,39 @@ func (_c *FeatureInterface_AddResponseCallback_Call) RunAndReturn(run func(model return _c } +// AddResultCallback provides a mock function with given fields: function +func (_m *FeatureInterface) AddResultCallback(function func(api.ResponseMessage)) { + _m.Called(function) +} + +// FeatureInterface_AddResultCallback_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddResultCallback' +type FeatureInterface_AddResultCallback_Call struct { + *mock.Call +} + +// AddResultCallback is a helper method to define mock.On call +// - function func(api.ResponseMessage) +func (_e *FeatureInterface_Expecter) AddResultCallback(function interface{}) *FeatureInterface_AddResultCallback_Call { + return &FeatureInterface_AddResultCallback_Call{Call: _e.mock.On("AddResultCallback", function)} +} + +func (_c *FeatureInterface_AddResultCallback_Call) Run(run func(function func(api.ResponseMessage))) *FeatureInterface_AddResultCallback_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(func(api.ResponseMessage))) + }) + return _c +} + +func (_c *FeatureInterface_AddResultCallback_Call) Return() *FeatureInterface_AddResultCallback_Call { + _c.Call.Return() + return _c +} + +func (_c *FeatureInterface_AddResultCallback_Call) RunAndReturn(run func(func(api.ResponseMessage))) *FeatureInterface_AddResultCallback_Call { + _c.Call.Return(run) + return _c +} + // Bind provides a mock function with given fields: func (_m *FeatureInterface) Bind() (*model.MsgCounterType, error) { ret := _m.Called() From d123aac1380087985e407222bfa30fbd1fc75798 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 13 Mar 2024 13:05:00 +0100 Subject: [PATCH 223/240] Update SHIP, SPINE --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ce3b24d1..78725430 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654 + github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da + github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index ccdd05e0..7277d886 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= -github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654 h1:5oHCw3s4igUttQwEpf5yRNly3GNSQtMX9V652b9VWlU= -github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da h1:+O2QSawKtKxDYgVip/C/KYcRBA0NrwwuwSIklsLDWnI= +github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 h1:DPyWFN3+6eAJwdA2nrLzXOXPSoe+mb0ccUFSVB8TI90= +github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From c2f35e1217be4c138e20a5171501fb3c9b5fa380 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 27 Mar 2024 13:47:56 +0100 Subject: [PATCH 224/240] Update SHIP --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 78725430..5f3ed07e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da + github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 7277d886..083400fa 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da h1:+O2QSawKtKxDYgVip/C/KYcRBA0NrwwuwSIklsLDWnI= -github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 h1:r71X9nuRTe2bUwWVRBpgaWII/oweFVWeM3DvgPtorAM= +github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 h1:DPyWFN3+6eAJwdA2nrLzXOXPSoe+mb0ccUFSVB8TI90= github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= From 2f6a203b9c99d23eb46756e6c66608866971fbbf Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Apr 2024 16:27:00 +0200 Subject: [PATCH 225/240] Update SPINE and apply fixes --- features/electricalconnection.go | 2 +- features/electricalconnection_test.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/features/electricalconnection.go b/features/electricalconnection.go index 9673cfcd..0d35e1de 100644 --- a/features/electricalconnection.go +++ b/features/electricalconnection.go @@ -295,7 +295,7 @@ func (e *ElectricalConnection) GetCharacteristics() ([]model.ElectricalConnectio return nil, api.ErrDataNotAvailable } - return data.ElectricalConnectionCharacteristicListData, nil + return data.ElectricalConnectionCharacteristicData, nil } // return characteristics for a Electrical Connections diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 91b1bf0a..516f5f41 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -374,7 +374,7 @@ func (s *ElectricalConnectionSuite) addDescription() { func (s *ElectricalConnectionSuite) addCharacteristics() { rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) fData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ { ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), diff --git a/go.mod b/go.mod index 5f3ed07e..69afabe9 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 - github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 + github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 083400fa..93392b55 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 h1:r71X9nuRTe2bUwWVRBpgaWII/oweFVWeM3DvgPtorAM= github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 h1:DPyWFN3+6eAJwdA2nrLzXOXPSoe+mb0ccUFSVB8TI90= -github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a h1:H34tdsbi4MIQYbZxYKdMx5fm37WsXmJbyD2R3vGo4EQ= +github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 0382e89114003202ed81f0fe1cbe1bf225a581fc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Apr 2024 19:00:36 +0200 Subject: [PATCH 226/240] Update SHIP --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 69afabe9..e7714289 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 + github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 93392b55..2e16b999 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 h1:r71X9nuRTe2bUwWVRBpgaWII/oweFVWeM3DvgPtorAM= -github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 h1:k89K2PYYVgAYbRZ3Kj+1AZ9Pgme7aOESXEhXSZ3qEHs= +github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a h1:H34tdsbi4MIQYbZxYKdMx5fm37WsXmJbyD2R3vGo4EQ= github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= From a390bc2437ddfa40baf96cdac63073e2a77672e5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 18:24:23 +0200 Subject: [PATCH 227/240] Update SHIP, SPINE --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index e7714289..1a362aec 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 - github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a + github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 + github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 2e16b999..703530a4 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 h1:k89K2PYYVgAYbRZ3Kj+1AZ9Pgme7aOESXEhXSZ3qEHs= -github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a h1:H34tdsbi4MIQYbZxYKdMx5fm37WsXmJbyD2R3vGo4EQ= -github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 h1:A4odLKYuqCfiv08Ths5gVkiDjgnHxhbVUhThizMucPA= +github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c h1:Hk48Nz21cfd2wXe1DHK5lB1mmW2PNs/WYzseTno7rTI= +github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From d7be76ca222ba96584d8070826b2f59a1732b663 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 18:24:36 +0200 Subject: [PATCH 228/240] Minor linter and typo fixes --- .golangci.yml | 58 +++++++++++++++++++++++++++ features/electricalconnection_test.go | 1 - service/service_test.go | 2 - 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..dd9bcca0 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,58 @@ +run: + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 5m + + # include test files or not, default is true + tests: true + + # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": + # If invoked with -mod=readonly, the go command is disallowed from the implicit + # automatic updating of go.mod described above. Instead, it fails when any changes + # to go.mod are needed. This setting is most useful to check that go.mod does + # not need updates, such as in a continuous integration and testing system. + # If invoked with -mod=vendor, the go command assumes that the vendor + # directory holds the correct copies of dependencies and ignores + # the dependency descriptions in go.mod. + modules-download-mode: readonly + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + formats: + - format: colored-line-number + +linters: + enable: + - bodyclose + - errcheck + - errorlint + - gocheckcompilerdirectives + - gochecknoinits + - gochecksumtype + - goconst + - gofmt + - gosimple + - gosec + - govet + - nilerr + - nilnil + - staticcheck + - typecheck + - unused + - whitespace + +issues: + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - errcheck + - goconst + - gosec + + # checking for errors in defers seldom makes sense... + - source: "^\\s*defer\\s" + linters: + - errcheck + - staticcheck diff --git a/features/electricalconnection_test.go b/features/electricalconnection_test.go index 516f5f41..e26b9dd6 100644 --- a/features/electricalconnection_test.go +++ b/features/electricalconnection_test.go @@ -244,7 +244,6 @@ func (s *ElectricalConnectionSuite) Test_GetPermittedValueSetsEmptyElli() { data, err = s.electricalConnection.GetPermittedValueSets() assert.Nil(s.T(), err) assert.NotNil(s.T(), data) - } func (s *ElectricalConnectionSuite) Test_GetPermittedValueSetForParameterId() { diff --git a/service/service_test.go b/service/service_test.go index 1aca3fd7..9e549646 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -121,7 +121,6 @@ func (s *ServiceSuite) Test_ConnectionsHub() { s.conHub.EXPECT().DisconnectSKI(mock.Anything, mock.Anything).Return() s.sut.DisconnectSKI(testSki, "reason") - } func (s *ServiceSuite) Test_SetLogging() { @@ -136,7 +135,6 @@ func (s *ServiceSuite) Test_SetLogging() { } func (s *ServiceSuite) Test_Setup() { - err := s.sut.Setup() assert.NotNil(s.T(), err) From 56359abacabe155a888271c30b5075df6cb5036b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 18 Apr 2024 21:27:12 +0200 Subject: [PATCH 229/240] Update SPINE --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1a362aec..4f092898 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 - github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c + github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 703530a4..4163383e 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 h1:A4odLKYuqCfiv08Ths5gVkiDjgnHxhbVUhThizMucPA= github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c h1:Hk48Nz21cfd2wXe1DHK5lB1mmW2PNs/WYzseTno7rTI= -github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44 h1:zyksXO307BwB1t1U5b6vgxw4Pnd0TlbYVouwZQQvrP8= +github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 7fdbd4b944aab34b3ae784d60238dbd691862d43 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 18 Apr 2024 21:29:08 +0200 Subject: [PATCH 230/240] Make sure to use UTC time --- features/devicediagnosis_test.go | 2 +- features/measurement_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/devicediagnosis_test.go b/features/devicediagnosis_test.go index 59fae296..08d0dcff 100644 --- a/features/devicediagnosis_test.go +++ b/features/devicediagnosis_test.go @@ -98,7 +98,7 @@ func (s *DeviceDiagnosisSuite) Test_IsHeartbeatWithinDuration() { result := s.deviceDiagnosis.IsHeartbeatWithinDuration(time.Second * 10) assert.Equal(s.T(), false, result) - now := time.Now() + now := time.Now().UTC() data := &model.DeviceDiagnosisHeartbeatDataType{ HeartbeatCounter: util.Ptr(uint64(1)), diff --git a/features/measurement_test.go b/features/measurement_test.go index d9a76d2c..03d28a96 100644 --- a/features/measurement_test.go +++ b/features/measurement_test.go @@ -218,7 +218,7 @@ func (s *MeasurementSuite) addConstraints() { func (s *MeasurementSuite) addData() { rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) - t := time.Now() + t := time.Now().UTC() fData := &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ { From 09d78db4dda1406f75aaad527039cb0c27e953d2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 18 Apr 2024 21:29:46 +0200 Subject: [PATCH 231/240] Fix heartbeat check --- features/devicediagnosis.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/features/devicediagnosis.go b/features/devicediagnosis.go index ce9785e4..7966d067 100644 --- a/features/devicediagnosis.go +++ b/features/devicediagnosis.go @@ -68,8 +68,7 @@ func (d *DeviceDiagnosis) IsHeartbeatWithinDuration(duration time.Duration) bool return false } - now := time.Now() - diff := now.Sub(timeValue) + diff := time.Now().UTC().Add(-1 * duration) - return diff < duration + return diff.Compare(timeValue.Local()) <= 0 } From 689039cfe6f90c0bdc875cdaed668ce7d58cebd9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 18 Apr 2024 21:30:07 +0200 Subject: [PATCH 232/240] Update helper method to be more flexible --- features/loadcontrol.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/features/loadcontrol.go b/features/loadcontrol.go index 0d51cf8e..16d08d14 100644 --- a/features/loadcontrol.go +++ b/features/loadcontrol.go @@ -82,6 +82,8 @@ func (l *LoadControl) GetLimitDescriptionsForCategory(category model.LoadControl // returns the load control limit descriptions of a provided type, direction and scope // returns an error if no description data for the category is available +// +// providing an empty string for any of the params, will ignore the value in the request func (l *LoadControl) GetLimitDescriptionsForTypeCategoryDirectionScope( limitType model.LoadControlLimitTypeType, limitCategory model.LoadControlCategoryType, @@ -97,14 +99,10 @@ func (l *LoadControl) GetLimitDescriptionsForTypeCategoryDirectionScope( for _, item := range data { if item.LimitId != nil && - item.LimitType != nil && - *item.LimitType == limitType && - item.LimitCategory != nil && - *item.LimitCategory == limitCategory && - item.LimitDirection != nil && - *item.LimitDirection == limitDirection && - item.ScopeType != nil && - *item.ScopeType == scope { + (limitType == "" || (item.LimitType != nil && *item.LimitType == limitType)) && + (limitCategory == "" || (item.LimitCategory != nil && *item.LimitCategory == limitCategory)) && + (limitDirection == "" || (item.LimitDirection != nil && *item.LimitDirection == limitDirection)) && + (scope == "" || (item.ScopeType != nil && *item.ScopeType == scope)) { result = append(result, item) } } From 10ce038bc0744396f59cae96249ad9c31eb1bf84 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 4 May 2024 13:03:18 +0200 Subject: [PATCH 233/240] Change mDNS default to GoZeroConf implementation This is because of https://github.com/enbility/ship-go/issues/24 --- api/configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/configuration.go b/api/configuration.go index 69b5280e..6949a6c5 100644 --- a/api/configuration.go +++ b/api/configuration.go @@ -103,7 +103,7 @@ func NewConfiguration( port: port, voltage: voltage, heartbeatTimeout: heartbeatTimeout, - mdnsProviderSelection: mdns.MdnsProviderSelectionAll, + mdnsProviderSelection: mdns.MdnsProviderSelectionGoZeroConfOnly, } if port == 0 { From 35cf34067973f3c528ea702506aeaa6e7e41c14a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 4 May 2024 13:09:12 +0200 Subject: [PATCH 234/240] Fix test for previous commit --- api/configuration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/configuration_test.go b/api/configuration_test.go index bce02d58..b08f1559 100644 --- a/api/configuration_test.go +++ b/api/configuration_test.go @@ -79,7 +79,7 @@ func (s *ConfigurationSuite) Test_Configuration() { assert.NotNil(s.T(), config) assert.Nil(s.T(), err) - assert.Equal(s.T(), mdns.MdnsProviderSelectionAll, config.MdnsProviderSelection()) + assert.Equal(s.T(), mdns.MdnsProviderSelectionGoZeroConfOnly, config.MdnsProviderSelection()) config.SetMdnsProviderSelection(mdns.MdnsProviderSelectionAvahiOnly) assert.Equal(s.T(), mdns.MdnsProviderSelectionAvahiOnly, config.MdnsProviderSelection()) From 033cd23978d412ca8197e913fc607ea90275b7f4 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 4 May 2024 15:24:11 +0200 Subject: [PATCH 235/240] Updated SHIP, SPINE and adjust pairing APIs - Remove `InitiateOrApprovePairingWithSKI` - Add `UnregisterRemoteSKI` - Change `RegisterRemoteSKI` to be positive only --- api/api.go | 17 ++----- cmd/evse/main.go | 4 +- cmd/hems/main.go | 4 +- go.mod | 4 +- go.sum | 8 ++-- mocks/FeatureInterface.go | 2 +- mocks/ServiceInterface.go | 85 ++++++++++++++++----------------- mocks/ServiceReaderInterface.go | 2 +- service/service.go | 17 +++---- service/service_test.go | 8 ++-- 10 files changed, 72 insertions(+), 79 deletions(-) diff --git a/api/api.go b/api/api.go index 4ecfe914..1da4288b 100644 --- a/api/api.go +++ b/api/api.go @@ -44,22 +44,15 @@ type ServiceInterface interface { // Returns the Service detail of a remote SKI RemoteServiceForSKI(ski string) *shipapi.ServiceDetails - // Sets the SKI as being paired or not - // - // This should be called before `Start` for any SKI that has been - // paired in a previous session - RegisterRemoteSKI(ski string, enable bool) + // Sets the SKI as being paired + RegisterRemoteSKI(ski string) + + // Sets the SKI as not being paired + UnregisterRemoteSKI(ski string) // Disconnect from a connected remote SKI DisconnectSKI(ski string, reason string) - // Triggers the pairing process for a SKI - // - // This should be called while the service is running and the end - // user selected to initiate the pairing process with a device - // or wants to approve an incoming pairing request - InitiateOrApprovePairingWithSKI(ski string) - // Cancels the pairing process for a SKI // // This should be called while the service is running and the end diff --git a/cmd/evse/main.go b/cmd/evse/main.go index 09771752..26f180b9 100644 --- a/cmd/evse/main.go +++ b/cmd/evse/main.go @@ -86,7 +86,7 @@ func (h *evse) run() { os.Exit(0) } - h.myService.RegisterRemoteSKI(remoteSki, true) + h.myService.RegisterRemoteSKI(remoteSki) h.myService.Start() // defer h.myService.Shutdown() @@ -106,8 +106,8 @@ func (h *evse) ServiceShipIDUpdate(ski string, shipdID string) {} func (h *evse) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { if ski == remoteSki && detail.State() == shipapi.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") - h.myService.RegisterRemoteSKI(ski, false) h.myService.CancelPairingWithSKI(ski) + h.myService.UnregisterRemoteSKI(ski) h.myService.Shutdown() os.Exit(0) } diff --git a/cmd/hems/main.go b/cmd/hems/main.go index f89c38fb..354805ec 100644 --- a/cmd/hems/main.go +++ b/cmd/hems/main.go @@ -86,7 +86,7 @@ func (h *hems) run() { os.Exit(0) } - h.myService.RegisterRemoteSKI(remoteSki, true) + h.myService.RegisterRemoteSKI(remoteSki) h.myService.Start() // defer h.myService.Shutdown() @@ -106,8 +106,8 @@ func (h *hems) ServiceShipIDUpdate(ski string, shipdID string) {} func (h *hems) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { if ski == remoteSki && detail.State() == shipapi.ConnectionStateRemoteDeniedTrust { fmt.Println("The remote service denied trust. Exiting.") - h.myService.RegisterRemoteSKI(ski, false) h.myService.CancelPairingWithSKI(ski) + h.myService.UnregisterRemoteSKI(ski) h.myService.Shutdown() os.Exit(0) } diff --git a/go.mod b/go.mod index 4f092898..8248d2e6 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 - github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44 + github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371 + github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 4163383e..63b78aa5 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 h1:A4odLKYuqCfiv08Ths5gVkiDjgnHxhbVUhThizMucPA= -github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44 h1:zyksXO307BwB1t1U5b6vgxw4Pnd0TlbYVouwZQQvrP8= -github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371 h1:9z6MITNW3lQigNEVFzwX/WeX1dyjRdsArCrK8jCzDRk= +github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe h1:u2AIEbiW1jR+XD2QwIjfwO4tE+f63U2VDL9PVA4/mD4= +github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/mocks/FeatureInterface.go b/mocks/FeatureInterface.go index 0a2abc75..6626b099 100644 --- a/mocks/FeatureInterface.go +++ b/mocks/FeatureInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/ServiceInterface.go b/mocks/ServiceInterface.go index 79b5c8a5..21f9bddb 100644 --- a/mocks/ServiceInterface.go +++ b/mocks/ServiceInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks @@ -140,39 +140,6 @@ func (_c *ServiceInterface_DisconnectSKI_Call) RunAndReturn(run func(string, str return _c } -// InitiateOrApprovePairingWithSKI provides a mock function with given fields: ski -func (_m *ServiceInterface) InitiateOrApprovePairingWithSKI(ski string) { - _m.Called(ski) -} - -// ServiceInterface_InitiateOrApprovePairingWithSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitiateOrApprovePairingWithSKI' -type ServiceInterface_InitiateOrApprovePairingWithSKI_Call struct { - *mock.Call -} - -// InitiateOrApprovePairingWithSKI is a helper method to define mock.On call -// - ski string -func (_e *ServiceInterface_Expecter) InitiateOrApprovePairingWithSKI(ski interface{}) *ServiceInterface_InitiateOrApprovePairingWithSKI_Call { - return &ServiceInterface_InitiateOrApprovePairingWithSKI_Call{Call: _e.mock.On("InitiateOrApprovePairingWithSKI", ski)} -} - -func (_c *ServiceInterface_InitiateOrApprovePairingWithSKI_Call) Run(run func(ski string)) *ServiceInterface_InitiateOrApprovePairingWithSKI_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *ServiceInterface_InitiateOrApprovePairingWithSKI_Call) Return() *ServiceInterface_InitiateOrApprovePairingWithSKI_Call { - _c.Call.Return() - return _c -} - -func (_c *ServiceInterface_InitiateOrApprovePairingWithSKI_Call) RunAndReturn(run func(string)) *ServiceInterface_InitiateOrApprovePairingWithSKI_Call { - _c.Call.Return(run) - return _c -} - // LocalDevice provides a mock function with given fields: func (_m *ServiceInterface) LocalDevice() spine_goapi.DeviceLocalInterface { ret := _m.Called() @@ -315,9 +282,9 @@ func (_c *ServiceInterface_PairingDetailForSki_Call) RunAndReturn(run func(strin return _c } -// RegisterRemoteSKI provides a mock function with given fields: ski, enable -func (_m *ServiceInterface) RegisterRemoteSKI(ski string, enable bool) { - _m.Called(ski, enable) +// RegisterRemoteSKI provides a mock function with given fields: ski +func (_m *ServiceInterface) RegisterRemoteSKI(ski string) { + _m.Called(ski) } // ServiceInterface_RegisterRemoteSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterRemoteSKI' @@ -327,14 +294,13 @@ type ServiceInterface_RegisterRemoteSKI_Call struct { // RegisterRemoteSKI is a helper method to define mock.On call // - ski string -// - enable bool -func (_e *ServiceInterface_Expecter) RegisterRemoteSKI(ski interface{}, enable interface{}) *ServiceInterface_RegisterRemoteSKI_Call { - return &ServiceInterface_RegisterRemoteSKI_Call{Call: _e.mock.On("RegisterRemoteSKI", ski, enable)} +func (_e *ServiceInterface_Expecter) RegisterRemoteSKI(ski interface{}) *ServiceInterface_RegisterRemoteSKI_Call { + return &ServiceInterface_RegisterRemoteSKI_Call{Call: _e.mock.On("RegisterRemoteSKI", ski)} } -func (_c *ServiceInterface_RegisterRemoteSKI_Call) Run(run func(ski string, enable bool)) *ServiceInterface_RegisterRemoteSKI_Call { +func (_c *ServiceInterface_RegisterRemoteSKI_Call) Run(run func(ski string)) *ServiceInterface_RegisterRemoteSKI_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) + run(args[0].(string)) }) return _c } @@ -344,7 +310,7 @@ func (_c *ServiceInterface_RegisterRemoteSKI_Call) Return() *ServiceInterface_Re return _c } -func (_c *ServiceInterface_RegisterRemoteSKI_Call) RunAndReturn(run func(string, bool)) *ServiceInterface_RegisterRemoteSKI_Call { +func (_c *ServiceInterface_RegisterRemoteSKI_Call) RunAndReturn(run func(string)) *ServiceInterface_RegisterRemoteSKI_Call { _c.Call.Return(run) return _c } @@ -539,6 +505,39 @@ func (_c *ServiceInterface_Start_Call) RunAndReturn(run func()) *ServiceInterfac return _c } +// UnregisterRemoteSKI provides a mock function with given fields: ski +func (_m *ServiceInterface) UnregisterRemoteSKI(ski string) { + _m.Called(ski) +} + +// ServiceInterface_UnregisterRemoteSKI_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnregisterRemoteSKI' +type ServiceInterface_UnregisterRemoteSKI_Call struct { + *mock.Call +} + +// UnregisterRemoteSKI is a helper method to define mock.On call +// - ski string +func (_e *ServiceInterface_Expecter) UnregisterRemoteSKI(ski interface{}) *ServiceInterface_UnregisterRemoteSKI_Call { + return &ServiceInterface_UnregisterRemoteSKI_Call{Call: _e.mock.On("UnregisterRemoteSKI", ski)} +} + +func (_c *ServiceInterface_UnregisterRemoteSKI_Call) Run(run func(ski string)) *ServiceInterface_UnregisterRemoteSKI_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ServiceInterface_UnregisterRemoteSKI_Call) Return() *ServiceInterface_UnregisterRemoteSKI_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_UnregisterRemoteSKI_Call) RunAndReturn(run func(string)) *ServiceInterface_UnregisterRemoteSKI_Call { + _c.Call.Return(run) + return _c +} + // UserIsAbleToApproveOrCancelPairingRequests provides a mock function with given fields: allow func (_m *ServiceInterface) UserIsAbleToApproveOrCancelPairingRequests(allow bool) { _m.Called(allow) diff --git a/mocks/ServiceReaderInterface.go b/mocks/ServiceReaderInterface.go index 1b1d76da..c7700837 100644 --- a/mocks/ServiceReaderInterface.go +++ b/mocks/ServiceReaderInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/service/service.go b/service/service.go index 1bf95831..31d60848 100644 --- a/service/service.go +++ b/service/service.go @@ -180,10 +180,16 @@ func (s *Service) RemoteServiceForSKI(ski string) *shipapi.ServiceDetails { return s.connectionsHub.ServiceForSKI(ski) } -// Sets the SKI as being paired or not +// Sets the SKI as being paired // and connect it if paired and not currently being connected -func (s *Service) RegisterRemoteSKI(ski string, enable bool) { - s.connectionsHub.RegisterRemoteSKI(ski, enable) +func (s *Service) RegisterRemoteSKI(ski string) { + s.connectionsHub.RegisterRemoteSKI(ski) +} + +// Sets the SKI as not being paired +// and disconnects it if connected +func (s *Service) UnregisterRemoteSKI(ski string) { + s.connectionsHub.UnregisterRemoteSKI(ski) } // Close a connection to a remote SKI @@ -191,11 +197,6 @@ func (s *Service) DisconnectSKI(ski string, reason string) { s.connectionsHub.DisconnectSKI(ski, reason) } -// Triggers the pairing process for a SKI -func (s *Service) InitiateOrApprovePairingWithSKI(ski string) { - s.connectionsHub.InitiateOrApprovePairingWithSKI(ski) -} - // Cancels the pairing process for a SKI func (s *Service) CancelPairingWithSKI(ski string) { s.connectionsHub.CancelPairingWithSKI(ski) diff --git a/service/service_test.go b/service/service_test.go index 9e549646..569c4ebd 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -110,11 +110,11 @@ func (s *ServiceSuite) Test_ConnectionsHub() { s.localDevice.EXPECT().SetupRemoteDevice(mock.Anything, s).Return(nil) s.sut.SetupRemoteDevice(testSki, s) - s.conHub.EXPECT().RegisterRemoteSKI(mock.Anything, mock.Anything).Return() - s.sut.RegisterRemoteSKI(testSki, true) + s.conHub.EXPECT().RegisterRemoteSKI(mock.Anything).Return() + s.sut.RegisterRemoteSKI(testSki) - s.conHub.EXPECT().InitiateOrApprovePairingWithSKI(mock.Anything).Return() - s.sut.InitiateOrApprovePairingWithSKI(testSki) + s.conHub.EXPECT().UnregisterRemoteSKI(mock.Anything).Return() + s.sut.UnregisterRemoteSKI(testSki) s.conHub.EXPECT().CancelPairingWithSKI(mock.Anything).Return() s.sut.CancelPairingWithSKI(testSki) From 7b3fd2f684b6780acdee6e624d7b99d2bdab9615 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 12 May 2024 17:40:59 +0200 Subject: [PATCH 236/240] Update SHIP and SPINE and adopt changes - Auto Accept is not a configuration any longer - Added methods the get current auto accept state and change it --- api/api.go | 8 ++++++++ api/configuration.go | 19 ------------------- api/configuration_test.go | 5 ----- go.mod | 4 ++-- go.sum | 8 ++++---- mocks/ServiceInterface.go | 33 +++++++++++++++++++++++++++++++++ service/service.go | 10 +++++++++- service/service_test.go | 5 +++++ 8 files changed, 61 insertions(+), 31 deletions(-) diff --git a/api/api.go b/api/api.go index 1da4288b..1f06faa2 100644 --- a/api/api.go +++ b/api/api.go @@ -41,6 +41,14 @@ type ServiceInterface interface { // Provide the current pairing state for a SKI PairingDetailForSki(ski string) *shipapi.ConnectionStateDetail + // Defines wether incoming pairing requests should be automatically accepted or not + // + // Default: false + SetAutoAccept(value bool) + + // Returns if the service has auto accept enabled or not + IsAutoAcceptEnabled() bool + // Returns the Service detail of a remote SKI RemoteServiceForSKI(ski string) *shipapi.ServiceDetails diff --git a/api/configuration.go b/api/configuration.go index 6949a6c5..9d5f40c8 100644 --- a/api/configuration.go +++ b/api/configuration.go @@ -64,15 +64,6 @@ type Configuration struct { // The certificate used for the service and its connections, required certificate tls.Certificate - // Wether remote devices should be automatically accepted - // If enabled will automatically search for other services with - // the same setting and automatically connect to them. - // Has to be set on configuring the service! - // TODO: if disabled, user verification needs to be implemented and supported - // the spec defines that this should have a timeout and be activate - // e.g via a physical button - registerAutoAccept bool - // The sites grid voltage // This is useful when e.g. power values are not available and therefor // need to be calculated using the current values @@ -207,12 +198,6 @@ func (s *Configuration) SetInterfaces(ifaces []string) { s.interfaces = ifaces } -// define wether this service should announce auto accept -// TODO: this needs to be redesigned! -func (s *Configuration) SetRegisterAutoAccept(auto bool) { - s.registerAutoAccept = auto -} - // generates a standard identifier used for mDNS ID and SHIP ID // Brand-Model-SerialNumber func (s *Configuration) generateIdentifier() string { @@ -257,10 +242,6 @@ func (s *Configuration) SetCertificate(cert tls.Certificate) { s.certificate = cert } -func (s *Configuration) RegisterAutoAccept() bool { - return s.registerAutoAccept -} - // return the sites predefined grid voltage func (s *Configuration) Voltage() float64 { return s.voltage diff --git a/api/configuration_test.go b/api/configuration_test.go index b08f1559..6b2e388c 100644 --- a/api/configuration_test.go +++ b/api/configuration_test.go @@ -91,11 +91,6 @@ func (s *ConfigurationSuite) Test_Configuration() { ifacesValue := config.Interfaces() assert.Equal(s.T(), ifaces, ifacesValue) - config.SetRegisterAutoAccept(true) - assert.Equal(s.T(), true, config.registerAutoAccept) - registerValue := config.RegisterAutoAccept() - assert.Equal(s.T(), true, registerValue) - id := config.generateIdentifier() assert.NotEqual(s.T(), "", id) diff --git a/go.mod b/go.mod index 8248d2e6..21feb3ef 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371 - github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe + github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 + github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 63b78aa5..f4f5d5af 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371 h1:9z6MITNW3lQigNEVFzwX/WeX1dyjRdsArCrK8jCzDRk= -github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe h1:u2AIEbiW1jR+XD2QwIjfwO4tE+f63U2VDL9PVA4/mD4= -github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 h1:iPs4/u/N5qf3oiRHPK0tBOAr0N2vQzHV28lPe1U9iHE= +github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7 h1:dNB7YGajeWQbSAgOg0NXqlq5SdmLh0AVUv+wHWuoWa4= +github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/mocks/ServiceInterface.go b/mocks/ServiceInterface.go index 21f9bddb..7b71bf71 100644 --- a/mocks/ServiceInterface.go +++ b/mocks/ServiceInterface.go @@ -363,6 +363,39 @@ func (_c *ServiceInterface_RemoteServiceForSKI_Call) RunAndReturn(run func(strin return _c } +// SetAutoAccept provides a mock function with given fields: value +func (_m *ServiceInterface) SetAutoAccept(value bool) { + _m.Called(value) +} + +// ServiceInterface_SetAutoAccept_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetAutoAccept' +type ServiceInterface_SetAutoAccept_Call struct { + *mock.Call +} + +// SetAutoAccept is a helper method to define mock.On call +// - value bool +func (_e *ServiceInterface_Expecter) SetAutoAccept(value interface{}) *ServiceInterface_SetAutoAccept_Call { + return &ServiceInterface_SetAutoAccept_Call{Call: _e.mock.On("SetAutoAccept", value)} +} + +func (_c *ServiceInterface_SetAutoAccept_Call) Run(run func(value bool)) *ServiceInterface_SetAutoAccept_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *ServiceInterface_SetAutoAccept_Call) Return() *ServiceInterface_SetAutoAccept_Call { + _c.Call.Return() + return _c +} + +func (_c *ServiceInterface_SetAutoAccept_Call) RunAndReturn(run func(bool)) *ServiceInterface_SetAutoAccept_Call { + _c.Call.Return(run) + return _c +} + // SetLogging provides a mock function with given fields: logger func (_m *ServiceInterface) SetLogging(logger logging.LoggingInterface) { _m.Called(logger) diff --git a/service/service.go b/service/service.go index 31d60848..e700b3ae 100644 --- a/service/service.go +++ b/service/service.go @@ -80,7 +80,6 @@ func (s *Service) Setup() error { s.localService = shipapi.NewServiceDetails(ski) s.localService.SetShipID(sd.Identifier()) s.localService.SetDeviceType(string(sd.DeviceType())) - s.localService.SetRegisterAutoAccept(sd.RegisterAutoAccept()) logging.Log().Info("Local SKI: ", ski) @@ -180,6 +179,15 @@ func (s *Service) RemoteServiceForSKI(ski string) *shipapi.ServiceDetails { return s.connectionsHub.ServiceForSKI(ski) } +func (s *Service) SetAutoAccept(value bool) { + s.localService.SetAutoAccept(value) + s.connectionsHub.SetAutoAccept(value) +} + +func (s *Service) IsAutoAcceptEnabled() bool { + return s.localService.AutoAccept() +} + // Sets the SKI as being paired // and connect it if paired and not currently being connected func (s *Service) RegisterRemoteSKI(ski string) { diff --git a/service/service_test.go b/service/service_test.go index 569c4ebd..dbccf1ce 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -99,6 +99,7 @@ func (s *ServiceSuite) Test_ConnectionsHub() { s.sut.connectionsHub = s.conHub s.sut.spineLocalDevice = s.localDevice + s.sut.localService = shipapi.NewServiceDetails(testSki) s.conHub.EXPECT().PairingDetailForSki(mock.Anything).Return(nil) s.sut.PairingDetailForSki(testSki) @@ -110,6 +111,10 @@ func (s *ServiceSuite) Test_ConnectionsHub() { s.localDevice.EXPECT().SetupRemoteDevice(mock.Anything, s).Return(nil) s.sut.SetupRemoteDevice(testSki, s) + s.conHub.EXPECT().SetAutoAccept(mock.Anything).Return() + s.sut.SetAutoAccept(true) + assert.True(s.T(), s.sut.IsAutoAcceptEnabled()) + s.conHub.EXPECT().RegisterRemoteSKI(mock.Anything).Return() s.sut.RegisterRemoteSKI(testSki) From a94167bca521f9709d14e88556aef77d7fd1f06c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 12 May 2024 17:49:12 +0200 Subject: [PATCH 237/240] Add SmartEnergyManagementPs feature --- features/smartenergymanagementps.go | 63 ++++++++++++++++ features/smartenergymanagementps_test.go | 95 ++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 features/smartenergymanagementps.go create mode 100644 features/smartenergymanagementps_test.go diff --git a/features/smartenergymanagementps.go b/features/smartenergymanagementps.go new file mode 100644 index 00000000..8d90a08d --- /dev/null +++ b/features/smartenergymanagementps.go @@ -0,0 +1,63 @@ +package features + +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type SmartEnergyManagementPs struct { + *Feature +} + +// Get a new Identification features helper +// +// - The feature on the local entity has to be of role client +// - The feature on the remote entity has to be of role server +func NewSmartEnergyManagementPs( + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface) (*SmartEnergyManagementPs, error) { + feature, err := NewFeature(model.FeatureTypeTypeSmartEnergyManagementPs, localEntity, remoteEntity) + if err != nil { + return nil, err + } + + i := &SmartEnergyManagementPs{ + Feature: feature, + } + + return i, nil +} + +// request FunctionTypeSmartEnergyManagementPsData from a remote entity +func (i *SmartEnergyManagementPs) RequestValues() (*model.MsgCounterType, error) { + return i.requestData(model.FunctionTypeSmartEnergyManagementPsData, nil, nil) +} + +// write SmartEnergyManagementPsData +// returns an error if this failed +func (l *SmartEnergyManagementPs) WriteValues(data *model.SmartEnergyManagementPsDataType) (*model.MsgCounterType, error) { + if data == nil { + return nil, api.ErrMissingData + } + + cmd := model.CmdType{ + Function: util.Ptr(model.FunctionTypeSmartEnergyManagementPsData), + Filter: []model.FilterType{*model.NewFilterTypePartial()}, + SmartEnergyManagementPsData: data, + } + + return l.remoteDevice.Sender().Write(l.featureLocal.Address(), l.featureRemote.Address(), cmd) +} + +// return current values for FunctionTypeSmartEnergyManagementPsData +func (i *SmartEnergyManagementPs) GetValues() (*model.SmartEnergyManagementPsDataType, error) { + data, err := spine.RemoteFeatureDataCopyOfType[*model.SmartEnergyManagementPsDataType](i.featureRemote, model.FunctionTypeSmartEnergyManagementPsData) + if err != nil { + return nil, api.ErrDataNotAvailable + } + + return data, nil +} diff --git a/features/smartenergymanagementps_test.go b/features/smartenergymanagementps_test.go new file mode 100644 index 00000000..fcca328f --- /dev/null +++ b/features/smartenergymanagementps_test.go @@ -0,0 +1,95 @@ +package features_test + +import ( + "testing" + + "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestSmartEnergyManagementPsSuite(t *testing.T) { + suite.Run(t, new(SmartEnergyManagementPsSuite)) +} + +type SmartEnergyManagementPsSuite struct { + suite.Suite + + localEntity spineapi.EntityLocalInterface + remoteEntity spineapi.EntityRemoteInterface + + smartenergymgmtps *features.SmartEnergyManagementPs + sentMessage []byte +} + +var _ shipapi.ShipConnectionDataWriterInterface = (*SmartEnergyManagementPsSuite)(nil) + +func (s *SmartEnergyManagementPsSuite) WriteShipMessageWithPayload(message []byte) { + s.sentMessage = message +} + +func (s *SmartEnergyManagementPsSuite) BeforeTest(suiteName, testName string) { + s.localEntity, s.remoteEntity = setupFeatures( + s.T(), + s, + []featureFunctions{ + { + featureType: model.FeatureTypeTypeSmartEnergyManagementPs, + functions: []model.FunctionType{ + model.FunctionTypeSmartEnergyManagementPsData, + }, + }, + }, + ) + + var err error + s.smartenergymgmtps, err = features.NewSmartEnergyManagementPs(s.localEntity, s.remoteEntity) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), s.smartenergymgmtps) +} + +func (s *SmartEnergyManagementPsSuite) Test_RequestValues() { + counter, err := s.smartenergymgmtps.RequestValues() + assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) +} + +func (s *SmartEnergyManagementPsSuite) Test_WriteValues() { + counter, err := s.smartenergymgmtps.WriteValues(nil) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), counter) + + data := &model.SmartEnergyManagementPsDataType{ + NodeScheduleInformation: &model.PowerSequenceNodeScheduleInformationDataType{}, + Alternatives: []model.SmartEnergyManagementPsAlternativesType{}, + } + counter, err = s.smartenergymgmtps.WriteValues(data) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), counter) +} + +func (s *SmartEnergyManagementPsSuite) Test_GetValues() { + value, err := s.smartenergymgmtps.GetValues() + assert.NotNil(s.T(), err) + assert.Nil(s.T(), value) + + s.addData() + + value, err = s.smartenergymgmtps.GetValues() + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) +} + +func (s *SmartEnergyManagementPsSuite) addData() { + rF := s.remoteEntity.FeatureOfAddress(util.Ptr(model.AddressFeatureType(1))) + + fData := &model.SmartEnergyManagementPsDataType{ + NodeScheduleInformation: &model.PowerSequenceNodeScheduleInformationDataType{}, + Alternatives: []model.SmartEnergyManagementPsAlternativesType{}, + } + rF.UpdateData(model.FunctionTypeSmartEnergyManagementPsData, fData, nil, nil) +} From e0841966938b28e2a4f91400a35a33fae0000de5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 16 May 2024 19:32:17 +0200 Subject: [PATCH 238/240] Update SPINE --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 21feb3ef..bdebf6d7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 - github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7 + github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index f4f5d5af..00a41960 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 h1:iPs4/u/N5qf3oiRHPK0tBOAr0N2vQzHV28lPe1U9iHE= github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7 h1:dNB7YGajeWQbSAgOg0NXqlq5SdmLh0AVUv+wHWuoWa4= -github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93 h1:ra+Eom9EQD9YN85WqZ9rmSTxWDi6j1R4SzHx+gHQCqs= +github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From ab722ccb30bb1df40d346508d90e80a8b48b04a0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 15:49:05 +0200 Subject: [PATCH 239/240] Update README badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be794342..ab25b701 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # eebus-go -[![Build Status](https://github.com/enbility/eebus-go/actions/workflows/default.yml/badge.svg?branch=dev)](https://github.com/enbility/eebus-go/actions/workflows/default.yml/badge.svg?branch=dev) +[![Build Status](https://github.com/enbility/eebus-go/actions/workflows/default.yml/badge.svg?branch=main)](https://github.com/enbility/eebus-go/actions/workflows/default.yml/badge.svg?branch=main) [![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4)](https://godoc.org/github.com/enbility/eebus-go) -[![Coverage Status](https://coveralls.io/repos/github/enbility/eebus-go/badge.svg?branch=dev)](https://coveralls.io/github/enbility/eebus-go?branch=dev) +[![Coverage Status](https://coveralls.io/repos/github/enbility/eebus-go/badge.svg?branch=main)](https://coveralls.io/github/enbility/eebus-go?branch=main) [![Go report](https://goreportcard.com/badge/github.com/enbility/eebus-go)](https://goreportcard.com/report/github.com/enbility/eebus-go) [![CodeFactor](https://www.codefactor.io/repository/github/enbility/eebus-go/badge)](https://www.codefactor.io/repository/github/enbility/eebus-go) From a62a3bce79f4aa20303e715a5da727c957c84ac7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 15:49:46 +0200 Subject: [PATCH 240/240] Update SHIP and SPINE --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index bdebf6d7..2dcebb03 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/eebus-go go 1.21.1 require ( - github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 - github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93 + github.com/enbility/ship-go v0.5.0 + github.com/enbility/spine-go v0.5.0 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 00a41960..f9865682 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 h1:iPs4/u/N5qf3oiRHPK0tBOAr0N2vQzHV28lPe1U9iHE= -github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93 h1:ra+Eom9EQD9YN85WqZ9rmSTxWDi6j1R4SzHx+gHQCqs= -github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/ship-go v0.5.0 h1:Uqol2XjzDOcvT8HUAE4B/59yqd3mxhpJJ/Q2eDHNGqc= +github.com/enbility/ship-go v0.5.0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.5.0 h1:3OQBl8gQPW/iuWmwcabmCIXDcFCP0RsDw7uP8BYUmaY= +github.com/enbility/spine-go v0.5.0/go.mod h1:8rXOJ7nTa4qrSRK0PpfavBXMztxi6l+h/IFpIVmHviM= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=