diff --git a/example/driver/simpledriver.go b/example/driver/simpledriver.go index 683454a29..dd37c804a 100644 --- a/example/driver/simpledriver.go +++ b/example/driver/simpledriver.go @@ -291,7 +291,7 @@ func (s *SimpleDriver) RemoveDevice(deviceName string, protocols map[string]mode // Discover triggers protocol specific device discovery, which is an asynchronous operation. // Devices found as part of this discovery operation are written to the channel devices. -func (s *SimpleDriver) Discover() { +func (s *SimpleDriver) Discover() error { proto := make(map[string]models.ProtocolProperties) proto["other"] = map[string]any{"Address": "simple02", "Port": 301} @@ -316,6 +316,7 @@ func (s *SimpleDriver) Discover() { time.Sleep(time.Duration(s.serviceConfig.SimpleCustom.Writable.DiscoverSleepDurationSecs) * time.Second) s.deviceCh <- res + return nil } func (s *SimpleDriver) ValidateDevice(device models.Device) error { diff --git a/internal/autodiscovery/autodiscovery.go b/internal/autodiscovery/autodiscovery.go index f7d42fac8..0cca4bdb3 100644 --- a/internal/autodiscovery/autodiscovery.go +++ b/internal/autodiscovery/autodiscovery.go @@ -1,6 +1,6 @@ // -*- Mode: Go; indent-tabs-mode: t -*- // -// Copyright (C) 2020 IOTech Ltd +// Copyright (C) 2020-2023 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -8,7 +8,6 @@ package autodiscovery import ( "context" - "fmt" "sync" "time" @@ -24,7 +23,7 @@ func BootstrapHandler( wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { - discovery := container.ProtocolDiscoveryFrom(dic.Get) + driver := container.ProtocolDriverFrom(dic.Get) lc := bootstrapContainer.LoggingClientFrom(dic.Get) configuration := container.ConfigurationFrom(dic.Get) var runDiscovery bool = true @@ -38,24 +37,20 @@ func BootstrapHandler( lc.Info("AutoDiscovery stopped: interval error in configuration") runDiscovery = false } - if discovery == nil { - lc.Info("AutoDiscovery stopped: ProtocolDiscovery not implemented") - runDiscovery = false - } if runDiscovery { wg.Add(1) go func() { defer wg.Done() - lc.Info(fmt.Sprintf("Starting auto-discovery with duration %v", duration)) - DiscoveryWrapper(discovery, lc) + lc.Infof("Starting auto-discovery with duration %v", duration) + DiscoveryWrapper(driver, lc) for { select { case <-ctx.Done(): return case <-time.After(duration): - DiscoveryWrapper(discovery, lc) + DiscoveryWrapper(driver, lc) } } }() diff --git a/internal/autodiscovery/discovery.go b/internal/autodiscovery/discovery.go index 61ca64a4f..ada0fa49f 100644 --- a/internal/autodiscovery/discovery.go +++ b/internal/autodiscovery/discovery.go @@ -1,6 +1,6 @@ // -*- Mode: Go; indent-tabs-mode: t -*- // -// Copyright (C) 2020 IOTech Ltd +// Copyright (C) 2020-2023 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -21,7 +21,7 @@ type discoveryLocker struct { var locker discoveryLocker -func DiscoveryWrapper(discovery interfaces.ProtocolDiscovery, lc logger.LoggingClient) { +func DiscoveryWrapper(driver interfaces.ProtocolDriver, lc logger.LoggingClient) { locker.mux.Lock() if locker.busy { lc.Info("another device discovery process is currently running") @@ -32,7 +32,10 @@ func DiscoveryWrapper(discovery interfaces.ProtocolDiscovery, lc logger.LoggingC locker.mux.Unlock() lc.Debug("protocol discovery triggered") - discovery.Discover() + err := driver.Discover() + if err != nil { + lc.Error("failed to trigger protocol discovery", err.Error()) + } // ReleaseLock locker.mux.Lock() diff --git a/internal/container/deviceservice.go b/internal/container/deviceservice.go index 6376c64ca..8c9065e6e 100644 --- a/internal/container/deviceservice.go +++ b/internal/container/deviceservice.go @@ -1,6 +1,6 @@ // -*- Mode: Go; indent-tabs-mode: t -*- // -// Copyright (C) 2020-2022 IOTech Ltd +// Copyright (C) 2020-2023 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -16,41 +16,17 @@ import ( // DeviceServiceName contains the name of device service struct in the DIC. var DeviceServiceName = di.TypeInstanceToName(models.DeviceService{}) -// ProtocolDiscoveryName contains the name of protocol discovery implementation in the DIC. -var ProtocolDiscoveryName = di.TypeInstanceToName((*interfaces.ProtocolDiscovery)(nil)) - // ProtocolDriverName contains the name of protocol driver implementation in the DIC. var ProtocolDriverName = di.TypeInstanceToName((*interfaces.ProtocolDriver)(nil)) // AutoEventManagerName contains the name of autoevent manager implementation in the DIC var AutoEventManagerName = di.TypeInstanceToName((*interfaces.AutoEventManager)(nil)) -// DeviceValidatorName contains the name of device validator implementation in the DIC. -var DeviceValidatorName = di.TypeInstanceToName((*interfaces.DeviceValidator)(nil)) - // DeviceServiceFrom helper function queries the DIC and returns device service struct. func DeviceServiceFrom(get di.Get) *models.DeviceService { return get(DeviceServiceName).(*models.DeviceService) } -// ProtocolDiscoveryFrom helper function queries the DIC and returns protocol discovery implementation. -func ProtocolDiscoveryFrom(get di.Get) interfaces.ProtocolDiscovery { - casted, ok := get(ProtocolDiscoveryName).(interfaces.ProtocolDiscovery) - if ok { - return casted - } - return nil -} - -// DeviceValidatorFrom helper function queries the DIC and returns device validator implementation. -func DeviceValidatorFrom(get di.Get) interfaces.DeviceValidator { - casted, ok := get(DeviceValidatorName).(interfaces.DeviceValidator) - if ok { - return casted - } - return nil -} - // ProtocolDriverFrom helper function queries the DIC and returns protocol driver implementation. func ProtocolDriverFrom(get di.Get) interfaces.ProtocolDriver { return get(ProtocolDriverName).(interfaces.ProtocolDriver) diff --git a/internal/controller/http/discovery.go b/internal/controller/http/discovery.go index 1437a7e27..17fcd4929 100644 --- a/internal/controller/http/discovery.go +++ b/internal/controller/http/discovery.go @@ -1,6 +1,6 @@ // -*- Mode: Go; indent-tabs-mode: t -*- // -// Copyright (C) 2020 IOTech Ltd +// Copyright (C) 2020-2023 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -32,13 +32,8 @@ func (c *RestController) Discovery(writer http.ResponseWriter, request *http.Req return } - discovery := container.ProtocolDiscoveryFrom(c.dic.Get) - if discovery == nil { - err := errors.NewCommonEdgeX(errors.KindNotImplemented, "protocolDiscovery not implemented", nil) - c.sendEdgexError(writer, request, err, common.ApiDiscoveryRoute) - return - } + driver := container.ProtocolDriverFrom(c.dic.Get) - go autodiscovery.DiscoveryWrapper(discovery, c.lc) + go autodiscovery.DiscoveryWrapper(driver, c.lc) c.sendResponse(writer, request, common.ApiDiscoveryRoute, nil, http.StatusAccepted) } diff --git a/internal/controller/messaging/validation.go b/internal/controller/messaging/validation.go index 37d78240f..7907e873f 100644 --- a/internal/controller/messaging/validation.go +++ b/internal/controller/messaging/validation.go @@ -59,30 +59,29 @@ func SubscribeDeviceValidation(ctx context.Context, dic *di.Container) errors.Ed responseTopic := common.BuildTopic(responseTopicPrefix, msgEnvelope.RequestID) - validator := container.DeviceValidatorFrom(dic.Get) - if validator != nil { - var deviceRequest requests.AddDeviceRequest - err = json.Unmarshal(msgEnvelope.Payload, &deviceRequest) + driver := container.ProtocolDriverFrom(dic.Get) + + var deviceRequest requests.AddDeviceRequest + err = json.Unmarshal(msgEnvelope.Payload, &deviceRequest) + if err != nil { + lc.Errorf("Failed to JSON decoding AddDeviceRequest: %s", err.Error()) + res := types.NewMessageEnvelopeWithError(msgEnvelope.RequestID, err.Error()) + err = messageBus.Publish(res, responseTopic) if err != nil { - lc.Errorf("Failed to JSON decoding AddDeviceRequest: %s", err.Error()) - res := types.NewMessageEnvelopeWithError(msgEnvelope.RequestID, err.Error()) - err = messageBus.Publish(res, responseTopic) - if err != nil { - lc.Errorf("Failed to publish device validation error response: %s", err.Error()) - } - continue + lc.Errorf("Failed to publish device validation error response: %s", err.Error()) } + continue + } - err = validator.ValidateDevice(dtos.ToDeviceModel(deviceRequest.Device)) + err = driver.ValidateDevice(dtos.ToDeviceModel(deviceRequest.Device)) + if err != nil { + lc.Errorf("Device validation failed: %s", err.Error()) + res := types.NewMessageEnvelopeWithError(msgEnvelope.RequestID, err.Error()) + err = messageBus.Publish(res, responseTopic) if err != nil { - lc.Errorf("Device validation failed: %s", err.Error()) - res := types.NewMessageEnvelopeWithError(msgEnvelope.RequestID, err.Error()) - err = messageBus.Publish(res, responseTopic) - if err != nil { - lc.Errorf("Failed to publish device validation error response: %s", err.Error()) - } - continue + lc.Errorf("Failed to publish device validation error response: %s", err.Error()) } + continue } res, err := types.NewMessageEnvelopeForResponse(nil, msgEnvelope.RequestID, msgEnvelope.CorrelationID, common.ContentTypeJSON) diff --git a/internal/controller/messaging/validation_test.go b/internal/controller/messaging/validation_test.go index ce76afcb8..ce101104c 100644 --- a/internal/controller/messaging/validation_test.go +++ b/internal/controller/messaging/validation_test.go @@ -67,16 +67,16 @@ func TestDeviceValidation(t *testing.T) { mockLogger.On("Debugf", mock.Anything, mock.Anything, mock.Anything).Return(nil) mockLogger.On("Errorf", mock.Anything, mock.Anything).Return(nil) - mockValidator := &mocks.DeviceValidator{} - mockValidator.On("ValidateDevice", dtos.ToDeviceModel(expectedDevice)).Return(nil) - mockValidator.On("ValidateDevice", dtos.ToDeviceModel(validationFailedDevice)).Return(errors.New("validation failed")) + mockDriver := &mocks.ProtocolDriver{} + mockDriver.On("ValidateDevice", dtos.ToDeviceModel(expectedDevice)).Return(nil) + mockDriver.On("ValidateDevice", dtos.ToDeviceModel(validationFailedDevice)).Return(errors.New("validation failed")) dic := di.NewContainer(di.ServiceConstructorMap{ container.ConfigurationName: func(get di.Get) any { return &config.ConfigurationStruct{} }, - container.DeviceValidatorName: func(get di.Get) any { - return mockValidator + container.ProtocolDriverName: func(get di.Get) any { + return mockDriver }, container.DeviceServiceName: func(get di.Get) any { return &models.DeviceService{Name: testServiceName} diff --git a/openapi/v3/device-sdk.yaml b/openapi/v3/device-sdk.yaml index d6e036d7a..019b06de4 100644 --- a/openapi/v3/device-sdk.yaml +++ b/openapi/v3/device-sdk.yaml @@ -571,12 +571,6 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - '501': - description: The device driver does not implement discovery. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' '503': description: Discovery is disabled by configuration. content: diff --git a/pkg/interfaces/devicevalidator.go b/pkg/interfaces/devicevalidator.go deleted file mode 100644 index c995ed911..000000000 --- a/pkg/interfaces/devicevalidator.go +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (C) 2022 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package interfaces - -import "github.com/edgexfoundry/go-mod-core-contracts/v3/models" - -// DeviceValidator is a low-level device-specific interface implemented -// by device services that validate device's protocol properties. -type DeviceValidator interface { - // ValidateDevice triggers device's protocol properties validation, returns error - // if validation failed and the incoming device will not be added into EdgeX. - ValidateDevice(device models.Device) error -} diff --git a/pkg/interfaces/mocks/DeviceValidator.go b/pkg/interfaces/mocks/DeviceValidator.go deleted file mode 100644 index 4a3d94ce9..000000000 --- a/pkg/interfaces/mocks/DeviceValidator.go +++ /dev/null @@ -1,42 +0,0 @@ -// Code generated by mockery v2.20.2. DO NOT EDIT. - -package mocks - -import ( - models "github.com/edgexfoundry/go-mod-core-contracts/v3/models" - mock "github.com/stretchr/testify/mock" -) - -// DeviceValidator is an autogenerated mock type for the DeviceValidator type -type DeviceValidator struct { - mock.Mock -} - -// ValidateDevice provides a mock function with given fields: device -func (_m *DeviceValidator) ValidateDevice(device models.Device) error { - ret := _m.Called(device) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Device) error); ok { - r0 = rf(device) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -type mockConstructorTestingTNewDeviceValidator interface { - mock.TestingT - Cleanup(func()) -} - -// NewDeviceValidator creates a new instance of DeviceValidator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewDeviceValidator(t mockConstructorTestingTNewDeviceValidator) *DeviceValidator { - mock := &DeviceValidator{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/interfaces/mocks/ProtocolDriver.go b/pkg/interfaces/mocks/ProtocolDriver.go index 23bc14c63..aa6cf42b6 100644 --- a/pkg/interfaces/mocks/ProtocolDriver.go +++ b/pkg/interfaces/mocks/ProtocolDriver.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.20.2. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package mocks @@ -30,6 +30,20 @@ func (_m *ProtocolDriver) AddDevice(deviceName string, protocols map[string]mode return r0 } +// Discover provides a mock function with given fields: +func (_m *ProtocolDriver) Discover() 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 +} + // HandleReadCommands provides a mock function with given fields: deviceName, protocols, reqs func (_m *ProtocolDriver) HandleReadCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []pkgmodels.CommandRequest) ([]*pkgmodels.CommandValue, error) { ret := _m.Called(deviceName, protocols, reqs) @@ -126,6 +140,20 @@ func (_m *ProtocolDriver) UpdateDevice(deviceName string, protocols map[string]m return r0 } +// ValidateDevice provides a mock function with given fields: device +func (_m *ProtocolDriver) ValidateDevice(device models.Device) error { + ret := _m.Called(device) + + var r0 error + if rf, ok := ret.Get(0).(func(models.Device) error); ok { + r0 = rf(device) + } else { + r0 = ret.Error(0) + } + + return r0 +} + type mockConstructorTestingTNewProtocolDriver interface { mock.TestingT Cleanup(func()) diff --git a/pkg/interfaces/protocoldiscovery.go b/pkg/interfaces/protocoldiscovery.go deleted file mode 100644 index 74ca33609..000000000 --- a/pkg/interfaces/protocoldiscovery.go +++ /dev/null @@ -1,18 +0,0 @@ -// -*- Mode: Go; indent-tabs-mode: t -*- -// -// Copyright (C) 2018 Canonical Ltd -// Copyright (C) 2020-2021 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package interfaces - -// ProtocolDiscovery is a low-level device-specific interface implemented -// by device services that support dynamic device discovery. -type ProtocolDiscovery interface { - // Discover triggers protocol specific device discovery, asynchronously - // writes the results to the channel which is passed to the implementation - // via ProtocolDriver.Initialize(). The results may be added to the device service - // based on a set of acceptance criteria (i.e. Provision Watchers). - Discover() -} diff --git a/pkg/interfaces/protocoldriver.go b/pkg/interfaces/protocoldriver.go index 59684db16..901f60498 100644 --- a/pkg/interfaces/protocoldriver.go +++ b/pkg/interfaces/protocoldriver.go @@ -54,4 +54,14 @@ type ProtocolDriver interface { // RemoveDevice is a callback function that is invoked // when a Device associated with this Device Service is removed RemoveDevice(deviceName string, protocols map[string]models.ProtocolProperties) error + + // Discover triggers protocol specific device discovery, asynchronously + // writes the results to the channel which is passed to the implementation + // via ProtocolDriver.Initialize(). The results may be added to the device service + // based on a set of acceptance criteria (i.e. Provision Watchers). + Discover() error + + // ValidateDevice triggers device's protocol properties validation, returns error + // if validation failed and the incoming device will not be added into EdgeX. + ValidateDevice(device models.Device) error } diff --git a/pkg/service/service.go b/pkg/service/service.go index f2e162ec7..a1235ff6b 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -55,8 +55,6 @@ type deviceService struct { serviceKey string lc logger.LoggingClient driver interfaces.ProtocolDriver - discovery interfaces.ProtocolDiscovery - validator interfaces.DeviceValidator autoEventManager interfaces.AutoEventManager controller *restController.RestController asyncCh chan *sdkModels.AsyncValues @@ -70,7 +68,7 @@ type deviceService struct { dic *di.Container } -func NewDeviceService(serviceKey string, serviceVersion string, driver any) (*deviceService, error) { +func NewDeviceService(serviceKey string, serviceVersion string, driver interfaces.ProtocolDriver) (*deviceService, error) { var service deviceService if serviceKey == "" { return nil, errors.New("please specify device service name") @@ -82,19 +80,7 @@ func NewDeviceService(serviceKey string, serviceVersion string, driver any) (*de } sdkCommon.ServiceVersion = serviceVersion - protocolDriver, ok := driver.(interfaces.ProtocolDriver) - if !ok { - return nil, errors.New("please implement and specify the ProtocolDriver") - } - service.driver = protocolDriver - - if discovery, ok := driver.(interfaces.ProtocolDiscovery); ok { - service.discovery = discovery - } - - if validator, ok := driver.(interfaces.DeviceValidator); ok { - service.validator = validator - } + service.driver = driver service.config = &config.ConfigurationStruct{} return &service, nil @@ -126,12 +112,6 @@ func (s *deviceService) Run() error { container.ProtocolDriverName: func(get di.Get) interface{} { return s.driver }, - container.ProtocolDiscoveryName: func(get di.Get) interface{} { - return s.discovery - }, - container.DeviceValidatorName: func(get di.Get) interface{} { - return s.validator - }, }) router := mux.NewRouter() diff --git a/pkg/startup/bootstrap.go b/pkg/startup/bootstrap.go index 6578d5ec3..c6223701d 100644 --- a/pkg/startup/bootstrap.go +++ b/pkg/startup/bootstrap.go @@ -11,10 +11,11 @@ import ( "fmt" "os" + "github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces" "github.com/edgexfoundry/device-sdk-go/v3/pkg/service" ) -func Bootstrap(serviceKey string, serviceVersion string, driver any) { +func Bootstrap(serviceKey string, serviceVersion string, driver interfaces.ProtocolDriver) { service, err := service.NewDeviceService(serviceKey, serviceVersion, driver) if err != nil { _, _ = fmt.Fprint(os.Stderr, err.Error())