diff --git a/api.md b/api.md deleted file mode 100644 index e739f74..0000000 --- a/api.md +++ /dev/null @@ -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 | -| 定值 | - | diff --git a/constants/measurement.go b/constants/measurement.go index 864b7e0..f16881f 100644 --- a/constants/measurement.go +++ b/constants/measurement.go @@ -29,3 +29,8 @@ const ( ChannelSuffixUBC = "UBC" ChannelSuffixUCA = "UCA" ) + +const ( + // MaxIdentifyHierarchy define max data indentify syntax hierarchy + MaxIdentifyHierarchy = 7 +) diff --git a/database/create_measurement.go b/database/create_measurement.go new file mode 100644 index 0000000..4085c94 --- /dev/null +++ b/database/create_measurement.go @@ -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 +} diff --git a/deploy/redis-test-data/measurments-recommend/measurement_injection.go b/deploy/redis-test-data/measurments-recommend/measurement_injection.go index fd756c7..c46a8ec 100644 --- a/deploy/redis-test-data/measurments-recommend/measurement_injection.go +++ b/deploy/redis-test-data/measurments-recommend/measurement_injection.go @@ -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 } diff --git a/handler/measurement_load.go b/handler/measurement_load.go index f90d362..134a3d9 100644 --- a/handler/measurement_load.go +++ b/handler/measurement_load.go @@ -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, diff --git a/handler/mesurement_create.go b/handler/mesurement_create.go new file mode 100644 index 0000000..ecd07c6 --- /dev/null +++ b/handler/mesurement_create.go @@ -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, + }, + }) +} diff --git a/main.go b/main.go index fa5b45b..f75a62d 100644 --- a/main.go +++ b/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) } } } diff --git a/model/redis_recommend.go b/model/redis_recommend.go index cb47814..e50fa65 100644 --- a/model/redis_recommend.go +++ b/model/redis_recommend.go @@ -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) } } diff --git a/network/circuit_diagram_create_request.go b/network/circuit_diagram_create_request.go index 63d04ac..56e8623 100644 --- a/network/circuit_diagram_create_request.go +++ b/network/circuit_diagram_create_request.go @@ -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"` diff --git a/network/measurement_request.go b/network/measurement_request.go index c04704a..6ed99bc 100644 --- a/network/measurement_request.go +++ b/network/measurement_request.go @@ -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"`