fix bug of new version of measurement recommend api
This commit is contained in:
parent
a21a423624
commit
3f70be0d1c
115
api.md
115
api.md
|
|
@ -1,115 +0,0 @@
|
|||
# 接口协议
|
||||
|
||||
## 实时数据接口示例
|
||||
|
||||
### 开启实时数据的订阅
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "start",
|
||||
"components": [
|
||||
{
|
||||
"interval": "1",
|
||||
"targets": [
|
||||
"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms",
|
||||
"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_B_rms"
|
||||
]
|
||||
},
|
||||
{
|
||||
"interval": "2",
|
||||
"targets": [
|
||||
"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_C_rms"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 实时数据订阅成功
|
||||
|
||||
```json
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"id": "grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms",
|
||||
"code": "1001",
|
||||
"msg": "subscription success"
|
||||
},
|
||||
{
|
||||
"id": "grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_B_rms",
|
||||
"code": "1002",
|
||||
"msg": "subscription failed"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 实时数据的返回
|
||||
|
||||
```json
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"id": "grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms",
|
||||
"datas": [
|
||||
{
|
||||
"time": 1736305467506000000,
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"time": 1736305467506000000,
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_B_rms",
|
||||
"datas": [
|
||||
{
|
||||
"time": 1736305467506000000,
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"time": 1736305467506000000,
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 结束实时数据的获取
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "stop",
|
||||
"targets": [
|
||||
"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms",
|
||||
"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_B_rms"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 实时数据状态值
|
||||
|
||||
| 动作描述 | 示例值 |
|
||||
| :--- | :--- |
|
||||
| 订阅成功 | 1001 |
|
||||
| 订阅失败 | 1002 |
|
||||
| 实时数据返回成功 | 1003 |
|
||||
| 实时数据返回失败 | 1004 |
|
||||
| 取消订阅成功 | 1005 |
|
||||
| 取消订阅失败 | 1006 |
|
||||
|
||||
## 实时数据标志
|
||||
|
||||
### 以设备语言中的type作为区分方式
|
||||
|
||||
| 标志描述 | 示例值 |
|
||||
| :--- | :--- |
|
||||
| 遥测 | TE |
|
||||
| 遥信 | TI |
|
||||
| 遥控 | TC |
|
||||
| 遥调 | TA |
|
||||
| 定值 | - |
|
||||
|
|
@ -29,3 +29,8 @@ const (
|
|||
ChannelSuffixUBC = "UBC"
|
||||
ChannelSuffixUCA = "UCA"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxIdentifyHierarchy define max data indentify syntax hierarchy
|
||||
MaxIdentifyHierarchy = 7
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
// Package database define database operation functions
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"modelRT/common/errcode"
|
||||
"modelRT/network"
|
||||
"modelRT/orm"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateMeasurement define create measurement info of the circuit diagram into DB
|
||||
func CreateMeasurement(ctx context.Context, tx *gorm.DB, measurementInfo network.MeasurementCreateInfo) (string, error) {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
globalUUID, err := uuid.FromString(measurementInfo.UUID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("format uuid from string type failed:%w", err)
|
||||
}
|
||||
|
||||
measurement := orm.Measurement{
|
||||
Tag: "",
|
||||
Name: "",
|
||||
Type: -1,
|
||||
Size: -1,
|
||||
DataSource: nil,
|
||||
EventPlan: nil,
|
||||
BayUUID: globalUUID,
|
||||
ComponentUUID: globalUUID,
|
||||
Op: -1,
|
||||
Ts: time.Now(),
|
||||
}
|
||||
|
||||
result := tx.WithContext(cancelCtx).Create(&measurement)
|
||||
if result.Error != nil || result.RowsAffected == 0 {
|
||||
err := result.Error
|
||||
if result.RowsAffected == 0 {
|
||||
err = fmt.Errorf("%w:please check insert component slice", errcode.ErrInsertRowUnexpected)
|
||||
}
|
||||
return "", fmt.Errorf("insert component info failed:%w", err)
|
||||
}
|
||||
return strconv.FormatInt(measurement.ID, 10), nil
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ func bulkInsertAllHierarchySets(ctx context.Context, rdb *redis.Client) error {
|
|||
return fmt.Errorf("dynamic hierarchy insertion failed: %w", err)
|
||||
}
|
||||
|
||||
log.Println("bulk insertion complete.")
|
||||
log.Println("bulk insertion complete")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func MeasurementGetHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"measurement_id": request.MeasurementID,
|
||||
"measurement_token": request.MeasurementToken,
|
||||
},
|
||||
|
|
@ -60,7 +60,7 @@ func MeasurementGetHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"measurement_id": request.MeasurementID,
|
||||
"measurement_token": request.MeasurementToken,
|
||||
"measurement_value": points,
|
||||
|
|
@ -72,7 +72,7 @@ func MeasurementGetHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"measurement_id": request.MeasurementID,
|
||||
"measurement_token": request.MeasurementToken,
|
||||
"measurement_info": measurementInfo,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
// Package handler provides HTTP handlers for various endpoints.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// MeasurementLinkCreateHandler defines the measurement link creation api
|
||||
func MeasurementLinkCreateHandler(c *gin.Context) {
|
||||
var request network.MeasurementCreateRequest
|
||||
|
||||
clientToken := c.GetString("client_token")
|
||||
if clientToken == "" {
|
||||
err := constants.ErrGetClientToken
|
||||
logger.Error(c, "failed to get client token from context", "error", err)
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
logger.Error(c, "failed to unmarshal measurement create request", "error", err)
|
||||
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: "Invalid request body format: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
pgClient := database.GetPostgresDBClient()
|
||||
var createInfo network.MeasurementCreateInfo
|
||||
newMeasurementID, err := database.CreateMeasurement(c, pgClient, createInfo)
|
||||
if err != nil {
|
||||
logger.Error(c, "failed to insert new measurement into postgres", "data", request.MeasurementData, "error", err)
|
||||
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
Msg: "Failed to create measurement record: " + err.Error(),
|
||||
Payload: map[string]any{
|
||||
"data_attempted": request.MeasurementData,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info(c, "successfully created new measurement record", "measurement_id", newMeasurementID)
|
||||
|
||||
c.JSON(http.StatusOK, network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "Measurement created successfully",
|
||||
Payload: map[string]any{
|
||||
"measurement_id": newMeasurementID,
|
||||
},
|
||||
})
|
||||
}
|
||||
6
main.go
6
main.go
|
|
@ -220,7 +220,7 @@ func main() {
|
|||
go func() {
|
||||
<-done
|
||||
if err := server.Shutdown(context.Background()); err != nil {
|
||||
logger.Error(ctx, "ShutdownServerError", "err", err)
|
||||
logger.Error(ctx, "shutdown serverError", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -229,10 +229,10 @@ func main() {
|
|||
if err != nil {
|
||||
if err == http.ErrServerClosed {
|
||||
// the service receives the shutdown signal normally and then closes
|
||||
logger.Info(ctx, "Server closed under request")
|
||||
logger.Info(ctx, "server closed under request")
|
||||
} else {
|
||||
// abnormal shutdown of service
|
||||
logger.Error(ctx, "Server closed unexpected", "err", err)
|
||||
logger.Error(ctx, "server closed unexpected", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, bool, er
|
|||
}
|
||||
|
||||
// start grid tagname fuzzy search
|
||||
recommends, err := runFuzzySearch(ctx, gridSearchInput, inputSliceLen)
|
||||
recommends, err := runFuzzySearch(ctx, gridSearchInput, "", inputSliceLen)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "fuzzy search failed for level 1", "search_input", gridSearchInput, "error", err)
|
||||
return []string{}, false, err
|
||||
|
|
@ -141,9 +141,10 @@ func getSpecificKeyByLength(inputLen int, input string) string {
|
|||
}
|
||||
|
||||
// handleLevelFuzzySearch define func to process recommendation logic for specific levels(level >= 2)
|
||||
func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, level int, keySetKey string, inputSlice []string) ([]string, bool, error) {
|
||||
searchInputIndex := level - 1
|
||||
func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy int, keySetKey string, inputSlice []string) ([]string, bool, error) {
|
||||
searchInputIndex := hierarchy - 1
|
||||
searchInput := inputSlice[searchInputIndex]
|
||||
searchPrefix := strings.Join(inputSlice[0:searchInputIndex], ".")
|
||||
|
||||
if searchInput == "" {
|
||||
var specificalKey string
|
||||
|
|
@ -152,11 +153,11 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, level int, k
|
|||
specificalKey = inputSlice[specificalKeyIndex]
|
||||
}
|
||||
|
||||
allResults, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, level, specificalKey)
|
||||
allResults, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, hierarchy, specificalKey)
|
||||
if err != nil {
|
||||
return []string{}, false, err
|
||||
}
|
||||
recommandResults := combineQueryResultByInput(level, inputSlice, allResults)
|
||||
recommandResults := combineQueryResultByInput(hierarchy, inputSlice, allResults)
|
||||
return recommandResults, isFuzzy, nil
|
||||
}
|
||||
|
||||
|
|
@ -167,25 +168,28 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, level int, k
|
|||
}
|
||||
|
||||
if keyExists {
|
||||
if hierarchy == constants.MaxIdentifyHierarchy {
|
||||
return []string{""}, false, nil
|
||||
}
|
||||
return []string{"."}, false, nil
|
||||
}
|
||||
|
||||
// start redis fuzzy search
|
||||
recommends, err := runFuzzySearch(ctx, searchInput, level)
|
||||
recommends, err := runFuzzySearch(ctx, searchInput, searchPrefix, hierarchy)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "fuzzy search failed for level", "level", level, "search_input", searchInput, "error", err)
|
||||
logger.Error(ctx, "fuzzy search failed by hierarchy", "hierarchy", hierarchy, "search_input", searchInput, "error", err)
|
||||
return []string{}, false, err
|
||||
}
|
||||
|
||||
if len(recommends) == 0 {
|
||||
logger.Error(ctx, "fuzzy search without result", "level", level, "search_input", searchInput, "error", err)
|
||||
logger.Error(ctx, "fuzzy search without result", "hierarchy", hierarchy, "search_input", searchInput, "error", err)
|
||||
return []string{}, true, nil
|
||||
}
|
||||
return combineQueryResultByInput(level, inputSlice, recommends), true, nil
|
||||
return combineQueryResultByInput(hierarchy, inputSlice, recommends), true, nil
|
||||
}
|
||||
|
||||
// runFuzzySearch define func to process redis fuzzy search
|
||||
func runFuzzySearch(ctx context.Context, searchInput string, inputSliceLen int) ([]string, error) {
|
||||
func runFuzzySearch(ctx context.Context, searchInput string, searchPrefix string, inputSliceLen int) ([]string, error) {
|
||||
searchInputLen := len(searchInput)
|
||||
|
||||
for searchInputLen != 0 {
|
||||
|
|
@ -210,8 +214,24 @@ func runFuzzySearch(ctx context.Context, searchInput string, inputSliceLen int)
|
|||
|
||||
var recommends []string
|
||||
for _, result := range results {
|
||||
termSlice := strings.Split(result.Term, ".")
|
||||
if len(termSlice) <= inputSliceLen {
|
||||
term := result.Term
|
||||
var termSliceLen int
|
||||
var termPrefix string
|
||||
|
||||
lastDotIndex := strings.LastIndex(term, ".")
|
||||
if lastDotIndex == -1 {
|
||||
termPrefix = ""
|
||||
} else {
|
||||
termPrefix = term[:lastDotIndex]
|
||||
}
|
||||
|
||||
if result.Term == "" {
|
||||
termSliceLen = 1
|
||||
} else {
|
||||
termSliceLen = strings.Count(result.Term, ".") + 1
|
||||
}
|
||||
|
||||
if termSliceLen == inputSliceLen && termPrefix == searchPrefix {
|
||||
recommends = append(recommends, result.Term)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ type TopologicUUIDCreateInfo struct {
|
|||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
// ComponentCreateInfo defines circuit diagram component create index info
|
||||
// ComponentCreateInfo defines circuit diagram component create info
|
||||
type ComponentCreateInfo struct {
|
||||
UUID string `json:"uuid"`
|
||||
Name string `json:"name"`
|
||||
|
|
@ -37,6 +37,20 @@ type ComponentCreateInfo struct {
|
|||
Op int `json:"op"`
|
||||
}
|
||||
|
||||
// MeasurementCreateInfo defines circuit diagram measurement create info
|
||||
type MeasurementCreateInfo struct {
|
||||
UUID string `json:"uuid"`
|
||||
Name string `json:"name"`
|
||||
Context map[string]any `json:"context"`
|
||||
GridID int64 `json:"grid_id"`
|
||||
ZoneID int64 `json:"zone_id"`
|
||||
StationID int64 `json:"station_id"`
|
||||
PageID int64 `json:"page_id"`
|
||||
Tag string `json:"tag"`
|
||||
Params string `json:"params"`
|
||||
Op int `json:"op"`
|
||||
}
|
||||
|
||||
// CircuitDiagramCreateRequest defines request params of circuit diagram create api
|
||||
type CircuitDiagramCreateRequest struct {
|
||||
PageID int64 `json:"page_id"`
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ type MeasurementGetRequest struct {
|
|||
MeasurementToken string `json:"token" example:"some-token"`
|
||||
}
|
||||
|
||||
// MeasurementCreateRequest defines the request payload for create an measurement
|
||||
type MeasurementCreateRequest struct {
|
||||
MeasurementData map[string]any `json:"measurement_data" example:""`
|
||||
}
|
||||
|
||||
// MeasurementRecommendRequest defines the request payload for an measurement recommend
|
||||
type MeasurementRecommendRequest struct {
|
||||
Input string `form:"input,omitempty" example:"grid1"`
|
||||
|
|
|
|||
Loading…
Reference in New Issue