198 lines
7.0 KiB
Go
198 lines
7.0 KiB
Go
|
// -*- Mode: Go; indent-tabs-mode: t -*-
|
||
|
//
|
||
|
// Copyright (C) 2020-2024 IOTech Ltd
|
||
|
//
|
||
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
|
||
|
package http
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"time"
|
||
|
|
||
|
"github.com/labstack/echo/v4"
|
||
|
|
||
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/application"
|
||
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/autodiscovery"
|
||
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/cache"
|
||
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/container"
|
||
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/controller/http/correlation"
|
||
|
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
|
||
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
|
||
|
commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v4/dtos/common"
|
||
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/dtos/requests"
|
||
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/errors"
|
||
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/models"
|
||
|
)
|
||
|
|
||
|
func (c *RestController) Discovery(e echo.Context) error {
|
||
|
request := e.Request()
|
||
|
writer := e.Response()
|
||
|
ctx := request.Context()
|
||
|
ds := container.DeviceServiceFrom(c.dic.Get)
|
||
|
if ds.AdminState == models.Locked {
|
||
|
err := errors.NewCommonEdgeX(errors.KindServiceLocked, "service locked", nil)
|
||
|
return c.sendEdgexError(writer, request, err, common.ApiDiscoveryRoute)
|
||
|
}
|
||
|
|
||
|
configuration := container.ConfigurationFrom(c.dic.Get)
|
||
|
if !configuration.Device.Discovery.Enabled {
|
||
|
err := errors.NewCommonEdgeX(errors.KindServiceUnavailable, "device discovery disabled", nil)
|
||
|
return c.sendEdgexError(writer, request, err, common.ApiDiscoveryRoute)
|
||
|
}
|
||
|
|
||
|
driver := container.ProtocolDriverFrom(c.dic.Get)
|
||
|
|
||
|
// Use correlation id as request id since there is no request body
|
||
|
requestId := correlation.IdFromContext(ctx)
|
||
|
go func() {
|
||
|
c.lc.Infof("Discovery triggered. Correlation Id: %s", requestId)
|
||
|
autodiscovery.DiscoveryWrapper(driver, ctx, c.dic)
|
||
|
c.lc.Infof("Discovery end. Correlation Id: %s", requestId)
|
||
|
}()
|
||
|
|
||
|
response := commonDTO.NewBaseResponse(requestId, "Device Discovery is triggered.", http.StatusAccepted)
|
||
|
return c.sendResponse(writer, request, common.ApiDiscoveryRoute, response, http.StatusAccepted)
|
||
|
}
|
||
|
|
||
|
func (c *RestController) ProfileScan(e echo.Context) error {
|
||
|
request := e.Request()
|
||
|
writer := e.Response()
|
||
|
ctx := request.Context()
|
||
|
if request.Body != nil {
|
||
|
defer func() { _ = request.Body.Close() }()
|
||
|
}
|
||
|
|
||
|
body, err := io.ReadAll(request.Body)
|
||
|
if err != nil {
|
||
|
edgexErr := errors.NewCommonEdgeX(errors.KindServerError, "Failed to read request body", err)
|
||
|
return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScanRoute)
|
||
|
}
|
||
|
|
||
|
extdriver := container.ExtendedProtocolDriverFrom(c.dic.Get)
|
||
|
if extdriver == nil {
|
||
|
edgexErr := errors.NewCommonEdgeX(errors.KindNotImplemented, "Profile scan is not implemented", nil)
|
||
|
return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScanRoute)
|
||
|
}
|
||
|
|
||
|
req, edgexErr := profileScanValidation(body, ctx, c.dic)
|
||
|
if edgexErr != nil {
|
||
|
return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScanRoute)
|
||
|
}
|
||
|
|
||
|
busy := make(chan bool)
|
||
|
go func() {
|
||
|
c.lc.Infof("Profile scanning is triggered. Correlation Id: %s", req.RequestId)
|
||
|
application.ProfileScanWrapper(busy, extdriver, req, ctx, c.dic)
|
||
|
c.lc.Infof("Profile scanning is end. Correlation Id: %s", req.RequestId)
|
||
|
}()
|
||
|
b := <-busy
|
||
|
if b {
|
||
|
edgexErr := errors.NewCommonEdgeX(errors.KindStatusConflict, fmt.Sprintf("Another profile scan process for %s is currently running", req.DeviceName), nil)
|
||
|
return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScanRoute)
|
||
|
}
|
||
|
|
||
|
response := commonDTO.NewBaseResponse(req.RequestId, "Device ProfileScan is triggered.", http.StatusAccepted)
|
||
|
return c.sendResponse(writer, request, common.ApiProfileScanRoute, response, http.StatusAccepted)
|
||
|
}
|
||
|
|
||
|
func profileScanValidation(request []byte, ctx context.Context, dic *di.Container) (requests.ProfileScanRequest, errors.EdgeX) {
|
||
|
var req requests.ProfileScanRequest
|
||
|
// check device service AdminState
|
||
|
ds := container.DeviceServiceFrom(dic.Get)
|
||
|
if ds.AdminState == models.Locked {
|
||
|
return req, errors.NewCommonEdgeX(errors.KindServiceLocked, "service locked", nil)
|
||
|
}
|
||
|
|
||
|
// parse request payload
|
||
|
err := req.UnmarshalJSON(request)
|
||
|
if err != nil {
|
||
|
return req, errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to parse request body", err)
|
||
|
}
|
||
|
|
||
|
// check requested device exists
|
||
|
_, exist := cache.Devices().ForName(req.DeviceName)
|
||
|
if !exist {
|
||
|
return req, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, fmt.Sprintf("device %s not found", req.DeviceName), nil)
|
||
|
}
|
||
|
|
||
|
// check profile should not exist
|
||
|
if len(req.ProfileName) > 0 {
|
||
|
if _, exist := cache.Profiles().ForName(req.ProfileName); exist {
|
||
|
return req, errors.NewCommonEdgeX(errors.KindStatusConflict, fmt.Sprintf("profile name %s is duplicated", req.ProfileName), nil)
|
||
|
}
|
||
|
} else {
|
||
|
req.ProfileName = fmt.Sprintf("%s_profile_%d", req.DeviceName, time.Now().UnixMilli())
|
||
|
}
|
||
|
|
||
|
requestId := req.RequestId
|
||
|
if len(requestId) == 0 {
|
||
|
// Use correlation id as request id if request id is not provided
|
||
|
requestId = correlation.IdFromContext(ctx)
|
||
|
}
|
||
|
|
||
|
req = requests.ProfileScanRequest{
|
||
|
BaseRequest: commonDTO.BaseRequest{
|
||
|
Versionable: commonDTO.NewVersionable(),
|
||
|
RequestId: requestId,
|
||
|
},
|
||
|
DeviceName: req.DeviceName,
|
||
|
ProfileName: req.ProfileName,
|
||
|
Options: req.Options,
|
||
|
}
|
||
|
|
||
|
return req, nil
|
||
|
}
|
||
|
|
||
|
func (c *RestController) StopDeviceDiscovery(e echo.Context) error {
|
||
|
request := e.Request()
|
||
|
writer := e.Response()
|
||
|
|
||
|
// URL parameters
|
||
|
requestId := e.Param(common.RequestId)
|
||
|
queryParams, err := url.ParseQuery(request.URL.RawQuery)
|
||
|
if err != nil {
|
||
|
edgexErr := errors.NewCommonEdgeX(errors.KindServerError, "failed to parse query parameter", err)
|
||
|
return c.sendEdgexErrorWithRequestId(writer, request, edgexErr, common.ApiDiscoveryByIdRoute, requestId)
|
||
|
}
|
||
|
options := make(map[string]any)
|
||
|
for key, vals := range queryParams {
|
||
|
options[key] = vals
|
||
|
}
|
||
|
edgexErr := autodiscovery.StopDeviceDiscovery(c.dic, requestId, options)
|
||
|
if edgexErr != nil {
|
||
|
return c.sendEdgexErrorWithRequestId(writer, request, edgexErr, common.ApiDiscoveryByIdRoute, requestId)
|
||
|
}
|
||
|
|
||
|
res := commonDTO.NewBaseResponse(requestId, "", http.StatusOK)
|
||
|
return c.sendResponse(writer, request, common.ApiDiscoveryByIdRoute, res, http.StatusOK)
|
||
|
}
|
||
|
|
||
|
func (c *RestController) StopProfileScan(e echo.Context) error {
|
||
|
request := e.Request()
|
||
|
writer := e.Response()
|
||
|
|
||
|
// URL parameters
|
||
|
deviceName := e.Param(common.Name)
|
||
|
queryParams, err := url.ParseQuery(request.URL.RawQuery)
|
||
|
if err != nil {
|
||
|
edgexErr := errors.NewCommonEdgeX(errors.KindServerError, "failed to parse query parameter", err)
|
||
|
return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScanByDeviceNameRoute)
|
||
|
}
|
||
|
options := make(map[string]any)
|
||
|
for key, vals := range queryParams {
|
||
|
options[key] = vals
|
||
|
}
|
||
|
edgexErr := application.StopProfileScan(c.dic, deviceName, options)
|
||
|
if edgexErr != nil {
|
||
|
return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScanByDeviceNameRoute)
|
||
|
}
|
||
|
|
||
|
res := commonDTO.NewBaseResponse("", "", http.StatusOK)
|
||
|
return c.sendResponse(writer, request, common.ApiProfileScanByDeviceNameRoute, res, http.StatusOK)
|
||
|
}
|