211 lines
6.4 KiB
Go
211 lines
6.4 KiB
Go
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
//
|
|
// Copyright (C) 2020-2025 IOTech Ltd
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
|
|
|
|
bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container"
|
|
"github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/controller"
|
|
"github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/handlers"
|
|
"github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/startup"
|
|
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
|
|
|
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/cache"
|
|
sdkCommon "github.com/edgexfoundry/device-sdk-go/v4/internal/common"
|
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/container"
|
|
restController "github.com/edgexfoundry/device-sdk-go/v4/internal/controller/http"
|
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/controller/messaging"
|
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/provision"
|
|
"github.com/edgexfoundry/device-sdk-go/v4/pkg/models"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// Bootstrap contains references to dependencies required by the BootstrapHandler.
|
|
type Bootstrap struct {
|
|
deviceService *deviceService
|
|
router *echo.Echo
|
|
}
|
|
|
|
// NewBootstrap is a factory method that returns an initialized Bootstrap receiver struct.
|
|
func NewBootstrap(ds *deviceService, router *echo.Echo) *Bootstrap {
|
|
return &Bootstrap{
|
|
deviceService: ds,
|
|
router: router,
|
|
}
|
|
}
|
|
|
|
func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, startupTimer startup.Timer, dic *di.Container) (success bool) {
|
|
s := b.deviceService
|
|
s.wg = wg
|
|
s.ctx = ctx
|
|
s.lc = bootstrapContainer.LoggingClientFrom(dic.Get)
|
|
s.autoEventManager = container.AutoEventManagerFrom(dic.Get)
|
|
s.commonController = controller.NewCommonController(dic, b.router, s.serviceKey, sdkCommon.ServiceVersion)
|
|
s.commonController.SetSDKVersion(sdkCommon.SDKVersion)
|
|
s.controller = restController.NewRestController(b.router, dic, s.serviceKey)
|
|
s.controller.InitRestRoutes(dic)
|
|
|
|
if !b.checkDependencyServiceAvailable(common.CoreMetaDataServiceKey, startupTimer) {
|
|
return false
|
|
}
|
|
|
|
edgexErr := cache.InitCache(s.serviceKey, s.baseServiceName, dic)
|
|
if edgexErr != nil {
|
|
s.lc.Errorf("Failed to init cache: %s", edgexErr.Error())
|
|
return false
|
|
}
|
|
|
|
devices := cache.Devices().All()
|
|
config := container.ConfigurationFrom(dic.Get)
|
|
reqFailsTracker := container.NewAllowedFailuresTracker()
|
|
for _, d := range devices {
|
|
reqFailsTracker.Set(d.Name, int(config.Device.AllowedFails))
|
|
}
|
|
dic.Update(di.ServiceConstructorMap{
|
|
container.AllowedRequestFailuresTrackerName: func(get di.Get) any {
|
|
return reqFailsTracker
|
|
},
|
|
})
|
|
|
|
if s.AsyncReadingsEnabled() {
|
|
s.asyncCh = make(chan *models.AsyncValues, s.config.Device.AsyncBufferSize)
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
s.processAsyncResults(ctx, dic)
|
|
}()
|
|
}
|
|
|
|
if s.DeviceDiscoveryEnabled() {
|
|
s.deviceCh = make(chan []models.DiscoveredDevice, 1)
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
s.processAsyncFilterAndAdd(ctx)
|
|
}()
|
|
}
|
|
|
|
err := s.driver.Initialize(s)
|
|
if err != nil {
|
|
s.lc.Errorf("ProtocolDriver init failed: %s", err.Error())
|
|
return false
|
|
}
|
|
|
|
edgexErr = s.selfRegister()
|
|
if edgexErr != nil {
|
|
s.lc.Errorf("Failed to register %s on Metadata: %s", s.serviceKey, edgexErr.Error())
|
|
return false
|
|
}
|
|
|
|
edgexErr = provision.LoadProfiles(s.config.Device.ProfilesDir, dic)
|
|
if edgexErr != nil {
|
|
s.lc.Errorf("Failed to load device profiles: %s", edgexErr.Error())
|
|
return false
|
|
}
|
|
|
|
edgexErr = provision.LoadDevices(s.config.Device.DevicesDir, dic)
|
|
if edgexErr != nil {
|
|
s.lc.Errorf("Failed to load devices: %s", edgexErr.Error())
|
|
return false
|
|
}
|
|
|
|
edgexErr = provision.LoadProvisionWatchers(s.config.Device.ProvisionWatchersDir, dic)
|
|
if edgexErr != nil {
|
|
s.lc.Errorf("Failed to load provision watchers: %s", edgexErr.Error())
|
|
return false
|
|
}
|
|
|
|
s.autoEventManager.StartAutoEvents()
|
|
|
|
// Very important that this bootstrap handler is called after the NewServiceMetrics handler so
|
|
// MetricsManager dependency has been created.
|
|
sdkCommon.InitializeSentMetrics(s.lc, dic)
|
|
return true
|
|
}
|
|
|
|
func (b *Bootstrap) checkDependencyServiceAvailable(serviceKey string, startupTimer startup.Timer) bool {
|
|
lc := b.deviceService.lc
|
|
registry := bootstrapContainer.RegistryFrom(b.deviceService.dic.Get)
|
|
mode := bootstrapContainer.DevRemoteModeFrom(b.deviceService.dic.Get)
|
|
clients := bootstrapContainer.ConfigurationFrom(b.deviceService.dic.Get).GetBootstrap().Clients
|
|
clientInfo, ok := (*clients)[serviceKey]
|
|
if !ok {
|
|
lc.Errorf("Client configuration for '%s' not found, missing common config? Use -cp or -cc flags for common config.", serviceKey)
|
|
return false
|
|
}
|
|
pingUrl := clientInfo.Url() + common.ApiPingRoute
|
|
|
|
var err error
|
|
for startupTimer.HasNotElapsed() {
|
|
if registry == nil || mode.InDevMode || mode.InRemoteMode {
|
|
lc.Debugf("Check service '%s' availability by Ping", serviceKey)
|
|
client := &http.Client{}
|
|
_, err = client.Get(pingUrl)
|
|
} else {
|
|
lc.Debugf("Check service '%s' availability via Registry", serviceKey)
|
|
_, err = registry.IsServiceAvailable(serviceKey)
|
|
}
|
|
if err == nil {
|
|
break
|
|
}
|
|
lc.Warnf("Check service '%s' availability failed: %s. retrying...", serviceKey, err.Error())
|
|
startupTimer.SleepForInterval()
|
|
}
|
|
|
|
if err != nil {
|
|
lc.Errorf("Check service '%s' availability time out: %s", serviceKey, err.Error())
|
|
return false
|
|
}
|
|
|
|
lc.Infof("Check service '%s' availability succeeded", serviceKey)
|
|
|
|
return true
|
|
}
|
|
|
|
func newMessageBusBootstrap(baseServiceName string) *messageBusBootstrap {
|
|
return &messageBusBootstrap{
|
|
baseServiceName: baseServiceName,
|
|
}
|
|
}
|
|
|
|
type messageBusBootstrap struct {
|
|
baseServiceName string
|
|
}
|
|
|
|
func (h *messageBusBootstrap) messageBusBootstrapHandler(ctx context.Context, wg *sync.WaitGroup, startupTimer startup.Timer, dic *di.Container) bool {
|
|
if !handlers.MessagingBootstrapHandler(ctx, wg, startupTimer, dic) {
|
|
return false
|
|
}
|
|
|
|
lc := bootstrapContainer.LoggingClientFrom(dic.Get)
|
|
err := messaging.SubscribeCommands(ctx, dic)
|
|
if err != nil {
|
|
lc.Errorf("Failed to subscribe internal command request: %v", err)
|
|
return false
|
|
}
|
|
|
|
err = messaging.MetadataSystemEventsCallback(ctx, h.baseServiceName, dic)
|
|
if err != nil {
|
|
lc.Errorf("Failed to subscribe Metadata system events: %v", err)
|
|
return false
|
|
}
|
|
|
|
err = messaging.SubscribeDeviceValidation(ctx, dic)
|
|
if err != nil {
|
|
lc.Errorf("Failed to subscribe device validation request: %v", err)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|