EdgexAgent/device-gps-go/internal/controller/messaging/callback.go

224 lines
8.9 KiB
Go
Raw Normal View History

2025-07-10 20:30:06 +08:00
//
// Copyright (C) 2023-2025 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0
package messaging
import (
"context"
"fmt"
"strings"
bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container"
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
"github.com/edgexfoundry/go-mod-core-contracts/v4/dtos"
"github.com/edgexfoundry/go-mod-core-contracts/v4/dtos/requests"
"github.com/edgexfoundry/go-mod-core-contracts/v4/errors"
"github.com/edgexfoundry/go-mod-messaging/v4/pkg/types"
"github.com/edgexfoundry/device-sdk-go/v4/internal/application"
"github.com/edgexfoundry/device-sdk-go/v4/internal/container"
)
func MetadataSystemEventsCallback(ctx context.Context, serviceBaseName string, dic *di.Container) errors.EdgeX {
lc := bootstrapContainer.LoggingClientFrom(dic.Get)
configuration := container.ConfigurationFrom(dic.Get)
messageBusInfo := container.ConfigurationFrom(dic.Get).MessageBus
deviceService := container.DeviceServiceFrom(dic.Get)
metadataSystemEventTopic := common.NewPathBuilder().EnableNameFieldEscape(configuration.Service.EnableNameFieldEscape).
SetPath(messageBusInfo.GetBaseTopicPrefix()).SetPath(common.MetadataSystemEventSubscribeTopic).SetNameFieldPath(deviceService.Name).SetPath("#").BuildPath()
profileDeleteSystemEventTopic := common.NewPathBuilder().EnableNameFieldEscape(configuration.Service.EnableNameFieldEscape).
SetPath(messageBusInfo.GetBaseTopicPrefix()).
SetPath(strings.Replace(common.MetadataSystemEventSubscribeTopic, "+/+", common.DeviceProfileSystemEventType+"/"+common.SystemEventActionDelete, 1)).
SetPath("#").BuildPath()
lc.Infof("Subscribing to System Events on topics: %s and %s", metadataSystemEventTopic, profileDeleteSystemEventTopic)
messages := make(chan types.MessageEnvelope, 1)
messageErrors := make(chan error, 1)
topics := []types.TopicChannel{
{
Topic: metadataSystemEventTopic,
Messages: messages,
},
{
Topic: profileDeleteSystemEventTopic,
Messages: messages,
},
}
// Must subscribe to provision watcher System Events separately when the service has an instance name. i.e. -i flag was used.
// This is because Provision Watchers apply to all instances of the device service, i.e. the service base name (without the instance portion).
// The above subscribe topic is for the specific instance name which is appropriate for Devices, Profiles and Devices service System Events
// and when the -i flag is not used.
if serviceBaseName != deviceService.Name {
// Must replace the first wildcard with the type for Provision Watchers
baseSubscribeTopic := strings.Replace(common.MetadataSystemEventSubscribeTopic, "+", common.ProvisionWatcherSystemEventType, 1)
provisionWatcherSystemEventSubscribeTopic := common.NewPathBuilder().EnableNameFieldEscape(configuration.Service.EnableNameFieldEscape).
SetPath(messageBusInfo.GetBaseTopicPrefix()).SetPath(baseSubscribeTopic).SetNameFieldPath(serviceBaseName).SetPath("#").BuildPath()
topics = append(topics, types.TopicChannel{
Topic: provisionWatcherSystemEventSubscribeTopic,
Messages: messages,
})
lc.Infof("Subscribing additionally to Provision Watcher System Events on topic: %s", provisionWatcherSystemEventSubscribeTopic)
}
messageBus := bootstrapContainer.MessagingClientFrom(dic.Get)
err := messageBus.Subscribe(topics, messageErrors)
if err != nil {
return errors.NewCommonEdgeXWrapper(err)
}
go func() {
for {
select {
case <-ctx.Done():
lc.Infof("Exiting waiting for MessageBus '%s' topic messages", metadataSystemEventTopic)
return
case err = <-messageErrors:
lc.Error(err.Error())
case msgEnvelope := <-messages:
lc.Debugf("System event received on message queue. Topic: %s, Correlation-id: %s", msgEnvelope.ReceivedTopic, msgEnvelope.CorrelationID)
var systemEvent dtos.SystemEvent
systemEvent, err := types.GetMsgPayload[dtos.SystemEvent](msgEnvelope)
if err != nil {
lc.Errorf("failed to JSON decoding system event: %s", err.Error())
continue
}
serviceName := container.DeviceServiceFrom(dic.Get).Name
if systemEvent.Owner == common.CoreMetaDataServiceKey {
if systemEvent.Type != common.DeviceProfileSystemEventType && systemEvent.Action != common.SystemEventActionDelete {
lc.Errorf("only support device profile delete system events from owner %s", systemEvent.Owner)
}
} else if systemEvent.Owner != serviceName && systemEvent.Owner != serviceBaseName {
lc.Errorf("unmatched system event owner %s with service name %s", systemEvent.Owner, serviceName)
continue
}
switch systemEvent.Type {
case common.DeviceSystemEventType:
err = deviceSystemEventAction(systemEvent, dic)
if err != nil {
lc.Error(err.Error(), common.CorrelationHeader, msgEnvelope.CorrelationID)
}
case common.DeviceProfileSystemEventType:
err = deviceProfileSystemEventAction(systemEvent, dic)
if err != nil {
lc.Error(err.Error(), common.CorrelationHeader, msgEnvelope.CorrelationID)
}
case common.ProvisionWatcherSystemEventType:
err = provisionWatcherSystemEventAction(systemEvent, dic)
if err != nil {
lc.Error(err.Error(), common.CorrelationHeader, msgEnvelope.CorrelationID)
}
case common.DeviceServiceSystemEventType:
err = deviceServiceSystemEventAction(systemEvent, dic)
if err != nil {
lc.Error(err.Error(), common.CorrelationHeader, msgEnvelope.CorrelationID)
}
default:
lc.Errorf("unknown system event type %s", systemEvent.Type)
continue
}
}
}
}()
return nil
}
func deviceSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Container) error {
var device dtos.Device
err := systemEvent.DecodeDetails(&device)
if err != nil {
return fmt.Errorf("failed to decode %s system event details: %s", systemEvent.Type, err.Error())
}
switch systemEvent.Action {
case common.SystemEventActionAdd:
err = application.AddDevice(requests.NewAddDeviceRequest(device), dic)
case common.SystemEventActionUpdate:
deviceModel := dtos.ToDeviceModel(device)
updateDeviceDTO := dtos.FromDeviceModelToUpdateDTO(deviceModel)
err = application.UpdateDevice(requests.NewUpdateDeviceRequest(updateDeviceDTO), dic)
case common.SystemEventActionDelete:
err = application.DeleteDevice(device.Name, dic)
default:
return fmt.Errorf("unknown %s system event action %s", systemEvent.Type, systemEvent.Action)
}
return err
}
func deviceProfileSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Container) error {
var deviceProfile dtos.DeviceProfile
err := systemEvent.DecodeDetails(&deviceProfile)
if err != nil {
return fmt.Errorf("failed to decode %s system event details: %s", systemEvent.Type, err.Error())
}
switch systemEvent.Action {
case common.SystemEventActionUpdate:
err = application.UpdateProfile(requests.NewDeviceProfileRequest(deviceProfile), dic)
case common.SystemEventActionDelete:
err = application.DeleteProfile(deviceProfile.Name, dic)
// there is no action needed for Device Profile Add in Device Service
case common.SystemEventActionAdd:
break
default:
return fmt.Errorf("unknown %s system event action %s", systemEvent.Type, systemEvent.Action)
}
return err
}
func provisionWatcherSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Container) error {
var pw dtos.ProvisionWatcher
err := systemEvent.DecodeDetails(&pw)
if err != nil {
return fmt.Errorf("failed to decode %s system event details: %s", systemEvent.Type, err.Error())
}
switch systemEvent.Action {
case common.SystemEventActionAdd:
err = application.AddProvisionWatcher(requests.NewAddProvisionWatcherRequest(pw), dic)
case common.SystemEventActionUpdate:
pwModel := dtos.ToProvisionWatcherModel(pw)
pwDTO := dtos.FromProvisionWatcherModelToUpdateDTO(pwModel)
err = application.UpdateProvisionWatcher(requests.NewUpdateProvisionWatcherRequest(pwDTO), dic)
case common.SystemEventActionDelete:
err = application.DeleteProvisionWatcher(pw.Name, dic)
default:
return fmt.Errorf("unknown %s system event action %s", systemEvent.Type, systemEvent.Action)
}
return err
}
func deviceServiceSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Container) error {
var deviceService dtos.DeviceService
err := systemEvent.DecodeDetails(&deviceService)
if err != nil {
return fmt.Errorf("failed to decode %s system event details: %s", systemEvent.Type, err.Error())
}
switch systemEvent.Action {
case common.SystemEventActionUpdate:
deviceServiceModel := dtos.ToDeviceServiceModel(deviceService)
updateDeviceServiceDTO := dtos.FromDeviceServiceModelToUpdateDTO(deviceServiceModel)
err = application.UpdateDeviceService(requests.NewUpdateDeviceServiceRequest(updateDeviceServiceDTO), dic)
// there is no action needed for Device Service Add and Delete in Device Service
case common.SystemEventActionAdd, common.SystemEventActionDelete:
break
default:
return fmt.Errorf("unknown %s system event action %s", systemEvent.Type, systemEvent.Action)
}
return err
}