Compare commits

...

21 Commits

Author SHA1 Message Date
douxu 6a1c42e22b modify drone pipeline code 2025-02-10 15:21:34 +08:00
douxu 08dc441385 add drone ci pipeline 2025-02-05 16:46:01 +08:00
douxu 58e54afed2 write new code of an alert event management handler 2025-01-23 14:56:01 +08:00
douxu 65f71348d6 design an alert event management structure 2025-01-22 16:38:46 +08:00
douxu 2b967450eb rewrite the real-time data acquisition and processing workflow 2025-01-21 16:35:44 +08:00
douxu 43dece39c1 add handler of dataRT real time data push 2025-01-20 16:20:21 +08:00
douxu 0520e9cece fix bug of test data with update handler 2025-01-13 15:54:40 +08:00
douxu 59574b4b90 fix bug of test data with update handler 2025-01-10 16:58:11 +08:00
douxu d89bf83f8b fix bug of create、delete、update handler 2025-01-10 16:57:29 +08:00
douxu c1691d4da2 fix bug of load data from postgres 2025-01-09 15:56:40 +08:00
douxu 655acf8e1e fix bug of circuit diagram load handler 2025-01-08 16:37:18 +08:00
douxu f48b527708 fix bug of circuit diagram load handler 2025-01-07 16:45:52 +08:00
douxu ac5d508171 add demo test sql and fix bug of modelRT running 2025-01-06 17:00:58 +08:00
douxu 2b4ad06b71 write comment of http handler 2024-12-31 16:18:51 +08:00
douxu 9385ba695c write code for adapter new component struct 2024-12-30 16:39:11 +08:00
douxu 5a9fa5cc4d write code for polling real time data from dataRT service 2024-12-26 15:03:20 +08:00
douxu 39e380ee1e optimize demo code 2024-12-25 16:34:57 +08:00
douxu c3f7ddf210 optimize demo code 2024-12-23 14:47:22 +08:00
douxu f8b9a70250 add replace anchor point api and optimize code of anchor param data polling function points 2024-12-20 16:06:42 +08:00
douxu efc15c3b2d wirte demo code 2024-12-18 16:25:49 +08:00
douxu a611c08c20 init modelRT show demo 2024-12-16 15:37:44 +08:00
61 changed files with 2018 additions and 608 deletions

12
.drone.yml Normal file
View File

@ -0,0 +1,12 @@
kind: pipeline
type: docker
name: default
steps:
- name: build
image: golang:latest
environment:
GO111MODULE: on
GOPROXY: https://goproxy.cn,direct
commands:
- go build main.go

100
alert/init.go Normal file
View File

@ -0,0 +1,100 @@
// Package alert define alert event struct of modelRT project
package alert
import (
"sort"
"sync"
"modelRT/constant"
)
var (
once sync.Once
_globalManagerMu sync.RWMutex
_globalManager *EventManager
)
// Event define alert event struct
type Event struct {
ComponentID int64
AnchorName string
Level constant.AlertLevel
Message string
StartTime int64
}
// EventManager define store and manager alert event struct
type EventManager struct {
mu sync.RWMutex
events map[constant.AlertLevel][]Event
}
// EventSet define alert event set implement sort.Interface
type EventSet []Event
func (es EventSet) Len() int {
return len(es)
}
func (es EventSet) Less(i, j int) bool {
return es[i].StartTime < es[j].StartTime
}
func (es EventSet) Swap(i, j int) {
es[i], es[j] = es[j], es[i]
}
// AddEvent define add a alert event to event manager
func (am *EventManager) AddEvent(event Event) {
am.mu.Lock()
defer am.mu.Unlock()
am.events[event.Level] = append(am.events[event.Level], event)
}
// GetEventsByLevel define get alert event by alert level
func (am *EventManager) GetEventsByLevel(level constant.AlertLevel) []Event {
am.mu.Lock()
defer am.mu.Unlock()
return am.events[level]
}
// GetRangeEventsByLevel define get range alert event by alert level
func (am *EventManager) GetRangeEventsByLevel(targetLevel constant.AlertLevel) []Event {
var targetEvents []Event
am.mu.Lock()
defer am.mu.Unlock()
for level, events := range am.events {
if targetLevel <= level {
targetEvents = append(targetEvents, events...)
}
}
sort.Sort(EventSet(targetEvents))
return targetEvents
}
// InitAlertEventManager define new alert event manager
func InitAlertEventManager() *EventManager {
return &EventManager{
events: make(map[constant.AlertLevel][]Event),
}
}
// InitAlertManagerInstance return instance of zap logger
func InitAlertManagerInstance() *EventManager {
once.Do(func() {
_globalManager = InitAlertEventManager()
})
return _globalManager
}
// GetAlertMangerInstance returns the global alert manager instance It's safe for concurrent use.
func GetAlertMangerInstance() *EventManager {
_globalManagerMu.RLock()
manager := _globalManager
_globalManagerMu.RUnlock()
return manager
}

View File

@ -0,0 +1,10 @@
package config
import "context"
// AnchorChanConfig define anchor params channel config struct
type AnchorChanConfig struct {
Ctx context.Context // 结束 context
AnchorChan chan AnchorParamConfig // 锚定参量实时值传递通道
ReadyChan chan struct{} // 就绪通知通道
}

View File

@ -0,0 +1,56 @@
// Package config define config struct of model runtime service
package config
import (
"modelRT/constant"
)
// AnchorParamListConfig define anchor params list config struct
type AnchorParamListConfig struct {
AnchorName string
FuncType string // 函数类型
UpperLimit float64 // 比较值上限
LowerLimit float64 // 比较值下限
}
// AnchorParamBaseConfig define anchor params base config struct
type AnchorParamBaseConfig struct {
ComponentID int64 // component表 ID
AnchorName string // 锚定参量名称
CompareValUpperLimit float64 // 比较值上限
CompareValLowerLimit float64 // 比较值下限
AnchorRealTimeData []float64 // 锚定参数实时值
}
// AnchorParamConfig define anchor params config struct
type AnchorParamConfig struct {
AnchorParamBaseConfig
CalculateFunc func(archorValue float64, args ...float64) float64 // 计算函数
CalculateParams []float64 // 计算参数
}
var baseVoltageFunc = func(archorValue float64, args ...float64) float64 {
voltage := archorValue
resistance := args[1]
return voltage / resistance
}
var baseCurrentFunc = func(archorValue float64, args ...float64) float64 {
current := archorValue
resistance := args[1]
return current * resistance
}
// SelectAnchorCalculateFuncAndParams define select anchor func and anchor calculate value by component type 、 anchor name and component data
func SelectAnchorCalculateFuncAndParams(componentType int, anchorName string, componentData map[string]interface{}) (func(archorValue float64, args ...float64) float64, []float64) {
if componentType == constant.DemoType {
if anchorName == "voltage" {
resistance := componentData["resistance"].(float64)
return baseVoltageFunc, []float64{resistance}
} else if anchorName == "current" {
resistance := componentData["resistance"].(float64)
return baseCurrentFunc, []float64{resistance}
}
}
return nil, []float64{}
}

View File

@ -45,7 +45,16 @@ type LoggerConfig struct {
// AntsConfig define config stuct of ants pool config
type AntsConfig struct {
ParseConcurrentQuantity int `mapstructure:"parse_concurrent_quantity"` // parse comtrade file concurrent quantity
ParseConcurrentQuantity int `mapstructure:"parse_concurrent_quantity"` // parse comtrade file concurrent quantity
RTDReceiveConcurrentQuantity int `mapstructure:"rtd_receive_concurrent_quantity"` // polling real time data concurrent quantity
}
// DataRTConfig define config stuct of data runtime server api config
type DataRTConfig struct {
Host string `mapstructure:"host"`
Port int64 `mapstructure:"port"`
PollingAPI string `mapstructure:"polling_api"`
Method string `mapstructure:"polling_api_method"`
}
// ModelRTConfig define config stuct of model runtime server
@ -55,10 +64,11 @@ type ModelRTConfig struct {
KafkaConfig `mapstructure:"kafka"`
LoggerConfig `mapstructure:"logger"`
AntsConfig `mapstructure:"ants"`
DataRTConfig `mapstructure:"dataRT"`
PostgresDBURI string `mapstructure:"-"`
}
// ReadAndInitConfig return wave record project config struct
// ReadAndInitConfig return modelRT project config struct
func ReadAndInitConfig(configDir, configName, configType string) (modelRTConfig ModelRTConfig) {
config := viper.New()
config.AddConfigPath(configDir)
@ -71,12 +81,11 @@ func ReadAndInitConfig(configDir, configName, configType string) (modelRTConfig
panic(err)
}
rtConfig := ModelRTConfig{}
if err := config.Unmarshal(&rtConfig); err != nil {
if err := config.Unmarshal(&modelRTConfig); err != nil {
panic(fmt.Sprintf("unmarshal modelRT config failed:%s\n", err.Error()))
}
modelRTConfig.PostgresDBURI = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s", rtConfig.Host, rtConfig.Port, rtConfig.User, rtConfig.Password, rtConfig.DataBase)
// init postgres db uri
modelRTConfig.PostgresDBURI = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s", modelRTConfig.PostgresConfig.Host, modelRTConfig.PostgresConfig.Port, modelRTConfig.PostgresConfig.User, modelRTConfig.PostgresConfig.Password, modelRTConfig.PostgresConfig.DataBase)
return modelRTConfig
}

View File

@ -1,7 +1,7 @@
postgres:
host: "192.168.2.156"
host: "192.168.2.103"
port: 5432
database: "circuit_diagram"
database: "demo"
user: "postgres"
password: "coslight"
@ -25,7 +25,7 @@ kafka:
logger:
mode: "development"
level: "debug"
filepath: "/home/douxu/log/wave_record-%s.log"
filepath: "/home/douxu/log/modelRT-%s.log"
maxsize: 1
maxbackups: 5
maxage: 30
@ -33,9 +33,17 @@ logger:
# ants config
ants:
parse_concurrent_quantity: 10
rtd_receive_concurrent_quantity: 10
# modelRT base config
base:
grid_id: 1
zone_id: 1
station_id: 1
# dataRT api config
dataRT:
host: "http://127.0.0.1"
port: 8888
polling_api: "datart/getPointData"
polling_api_method: "GET"

View File

@ -10,4 +10,5 @@ import (
type ModelParseConfig struct {
ComponentInfo orm.Component
Context context.Context
AnchorChan chan AnchorParamConfig
}

57
constant/alert.go Normal file
View File

@ -0,0 +1,57 @@
// Package constant define alert level constant
package constant
// AlertLevel define alert level type
type AlertLevel int
const (
// AllAlertLevel define all alert level
AllAlertLevel AlertLevel = iota
// InfoAlertLevel define info alert level
InfoAlertLevel
// WarningAlertLevel define warning alert level
WarningAlertLevel
// ErrorAlertLevel define error alert level
ErrorAlertLevel
// FatalAlertLevel define fatal alert level
FatalAlertLevel
)
func (a AlertLevel) String() string {
switch a {
case AllAlertLevel:
return "ALL"
case InfoAlertLevel:
return "INFO"
case WarningAlertLevel:
return "WARNING"
case ErrorAlertLevel:
return "ERROR"
case FatalAlertLevel:
return "FATAL"
default:
return "Unknown"
}
}
func (a AlertLevel) LevelCompare(b AlertLevel) bool {
return a <= b
}
// // AlertLevelFromString convert string to alert level
// func AlertLevelFromString(level int64) AlertLevel {
// switch level {
// case :
// return AllAlertLevel
// case "INFO":
// return InfoAlertLevel
// case "WARNING":
// return WarningAlertLevel
// case "ERROR":
// return ErrorAlertLevel
// case "FATAL":
// return FatalAlertLevel
// default:
// return AllAlertLevel
// }
// }

View File

@ -8,4 +8,6 @@ const (
BusbarType
// AsyncMotorType 异步电动机类型
AsyncMotorType
// DemoType Demo类型
DemoType
)

View File

@ -11,6 +11,9 @@ var ErrUpdateRowZero = errors.New("update affected rows is zero")
// ErrDeleteRowZero define error of delete affected row zero
var ErrDeleteRowZero = errors.New("delete affected rows is zero")
// ErrQueryRowZero define error of query affected row zero
var ErrQueryRowZero = errors.New("query affected rows is zero")
// ErrInsertRowUnexpected define error of insert affected row not reach expected number
var ErrInsertRowUnexpected = errors.New("the number of inserted data rows don't reach the expected value")

View File

@ -2,8 +2,8 @@
package constant
const (
// DevelopmentLogMode define development operator environment for wave record project
// DevelopmentLogMode define development operator environment for modelRT project
DevelopmentLogMode = "development"
// ProductionLogMode define production operator environment for wave record project
// ProductionLogMode define production operator environment for modelRT project
ProductionLogMode = "production"
)

View File

@ -4,6 +4,7 @@ package database
import (
"context"
"fmt"
"strconv"
"time"
"modelRT/constant"
@ -15,43 +16,36 @@ import (
)
// CreateComponentIntoDB define create component info of the circuit diagram into DB
func CreateComponentIntoDB(ctx context.Context, tx *gorm.DB, componentInfos []network.ComponentCreateInfo) error {
func CreateComponentIntoDB(ctx context.Context, tx *gorm.DB, componentInfo network.ComponentCreateInfo) (int64, error) {
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
var componentSlice []orm.Component
for _, info := range componentInfos {
globalUUID, err := uuid.FromString(info.UUID)
if err != nil {
return fmt.Errorf("format uuid from string type failed:%w", err)
}
componentInfo := orm.Component{
GlobalUUID: globalUUID,
GridID: info.GridID,
ZoneID: info.ZoneID,
StationID: info.StationID,
ComponentType: info.ComponentType,
State: info.State,
ConnectedBus: info.ConnectedBus,
Name: info.Name,
VisibleID: info.Name,
Description: info.Description,
Context: info.Context,
Comment: info.Comment,
InService: info.InService,
}
componentSlice = append(componentSlice, componentInfo)
globalUUID, err := uuid.FromString(componentInfo.UUID)
if err != nil {
return -1, fmt.Errorf("format uuid from string type failed:%w", err)
}
result := tx.WithContext(cancelCtx).Create(&componentSlice)
component := orm.Component{
GlobalUUID: globalUUID,
GridID: strconv.FormatInt(componentInfo.GridID, 10),
ZoneID: strconv.FormatInt(componentInfo.ZoneID, 10),
StationID: strconv.FormatInt(componentInfo.StationID, 10),
PageID: componentInfo.PageID,
Tag: componentInfo.Tag,
ComponentType: componentInfo.ComponentType,
Name: componentInfo.Name,
Context: componentInfo.Context,
Op: componentInfo.Op,
Ts: time.Now(),
}
if result.Error != nil || result.RowsAffected != int64(len(componentSlice)) {
result := tx.WithContext(cancelCtx).Create(&component)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected != int64(len(componentSlice)) {
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check insert component slice", constant.ErrInsertRowUnexpected)
}
return fmt.Errorf("insert component info failed:%w", err)
return -1, fmt.Errorf("insert component info failed:%w", err)
}
return nil
return component.ID, nil
}

View File

@ -8,37 +8,29 @@ import (
"modelRT/constant"
"modelRT/model"
"modelRT/network"
"github.com/gofrs/uuid"
jsoniter "github.com/json-iterator/go"
"gorm.io/gorm"
)
// CreateModelIntoDB define create component model params of the circuit diagram into DB
func CreateModelIntoDB(ctx context.Context, tx *gorm.DB, componentInfos []network.ComponentCreateInfo) error {
func CreateModelIntoDB(ctx context.Context, tx *gorm.DB, componentID int64, componentType int, modelParas string) error {
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
for _, componentInfo := range componentInfos {
modelStruct := model.SelectModelByType(componentInfo.ComponentType)
globalUUID, err := uuid.FromString(componentInfo.UUID)
if err != nil {
return fmt.Errorf("format uuid from string type failed:%w", err)
}
modelStruct.SetUUID(globalUUID)
err = jsoniter.Unmarshal([]byte(componentInfo.Params), modelStruct)
if err != nil {
return fmt.Errorf("unmarshal component model params failed:%w", err)
}
modelStruct := model.SelectModelByType(componentType)
modelStruct.SetComponentID(componentID)
err := jsoniter.Unmarshal([]byte(modelParas), modelStruct)
if err != nil {
return fmt.Errorf("unmarshal component model params failed:%w", err)
}
result := tx.Model(modelStruct).WithContext(cancelCtx).Create(modelStruct)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check insert model params", constant.ErrInsertRowUnexpected)
}
return fmt.Errorf("insert component model params into table %s failed:%w", modelStruct.ReturnTableName(), err)
result := tx.Model(modelStruct).WithContext(cancelCtx).Create(modelStruct)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check insert model params", constant.ErrInsertRowUnexpected)
}
return fmt.Errorf("insert component model params into table %s failed:%w", modelStruct.ReturnTableName(), err)
}
return nil
}

View File

@ -3,32 +3,32 @@ package database
import (
"context"
"fmt"
"strconv"
"time"
"modelRT/config"
"modelRT/diagram"
"modelRT/orm"
"github.com/gofrs/uuid"
"github.com/panjf2000/ants/v2"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// QueryCircuitDiagramComponentFromDB return the result of query circuit diagram component info order by page id from postgresDB
func QueryCircuitDiagramComponentFromDB(ctx context.Context, pool *ants.PoolWithFunc, logger *zap.Logger) error {
var Components []orm.Component
func QueryCircuitDiagramComponentFromDB(ctx context.Context, tx *gorm.DB, pool *ants.PoolWithFunc, logger *zap.Logger) error {
var components []orm.Component
// ctx超时判断
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
result := _globalPostgresClient.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Find(&Components)
result := tx.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Find(&components)
if result.Error != nil {
logger.Error("query circuit diagram component info failed", zap.Error(result.Error))
return result.Error
}
for _, component := range Components {
for _, component := range components {
pool.Invoke(config.ModelParseConfig{
ComponentInfo: component,
Context: ctx,
@ -37,27 +37,16 @@ func QueryCircuitDiagramComponentFromDB(ctx context.Context, pool *ants.PoolWith
return nil
}
// QueryElectricalEquipmentUUID return the result of query electrical equipment uuid from postgresDB by circuit diagram id info
func QueryElectricalEquipmentUUID(ctx context.Context, diagramID int64, logger *zap.Logger) error {
var uuids []string
// QueryComponentByUUID return the result of query circuit diagram component info by uuid from postgresDB
func QueryComponentByUUID(ctx context.Context, tx *gorm.DB, uuid uuid.UUID) (orm.Component, error) {
var component orm.Component
// ctx超时判断
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
tableName := "circuit_diagram_" + strconv.FormatInt(diagramID, 10)
result := _globalPostgresClient.Table(tableName).WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Select("uuid").Find(&uuids)
result := tx.WithContext(cancelCtx).Where("global_uuid = ? ", uuid).Clauses(clause.Locking{Strength: "UPDATE"}).Find(&component)
if result.Error != nil {
logger.Error("query circuit diagram overview info failed", zap.Error(result.Error))
return result.Error
return orm.Component{}, result.Error
}
for _, uuid := range uuids {
diagramParamsMap, err := diagram.GetComponentMap(uuid)
if err != nil {
logger.Error("get electrical circuit diagram overview info failed", zap.Error(result.Error))
return result.Error
}
fmt.Println(diagramParamsMap, err)
}
return nil
return component, nil
}

View File

@ -8,17 +8,18 @@ import (
"modelRT/orm"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// QueryAllPages return the all page info of the circuit diagram query by grid_id and zone_id and station_id
func QueryAllPages(ctx context.Context, logger *zap.Logger, gridID, zoneID, stationID int64) ([]orm.Page, error) {
func QueryAllPages(ctx context.Context, tx *gorm.DB, logger *zap.Logger, gridID, zoneID, stationID int64) ([]orm.Page, error) {
var pages []orm.Page
// ctx超时判断
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
result := _globalPostgresClient.Model(&orm.Page{}).WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Select(`"Page".id, "Page".Name, "Page".status,"Page".context`).Joins(`inner join "Station" on "Station".id = "Page".station_id`).Joins(`inner join "Zone" on "Zone".id = "Station".zone_id`).Joins(`inner join "Grid" on "Grid".id = "Zone".grid_id`).Where(`"Grid".id = ? and "Zone".id = ? and "Station".id = ?`, gridID, zoneID, stationID).Scan(&pages)
result := tx.Model(&orm.Page{}).WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Select(`"page".id, "page".Name, "page".status,"page".context`).Joins(`inner join "station" on "station".id = "page".station_id`).Joins(`inner join "zone" on "zone".id = "station".zone_id`).Joins(`inner join "grid" on "grid".id = "zone".grid_id`).Where(`"grid".id = ? and "zone".id = ? and "station".id = ?`, gridID, zoneID, stationID).Scan(&pages)
if result.Error != nil {
logger.Error("query circuit diagram pages by gridID and zoneID and stationID failed", zap.Int64("grid_id", gridID), zap.Int64("zone_id", zoneID), zap.Int64("station_id", stationID), zap.Error(result.Error))

View File

@ -11,16 +11,18 @@ import (
"github.com/gofrs/uuid"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// QueryTopologicByPageID return the topologic info of the circuit diagram query by pageID
func QueryTopologicByPageID(ctx context.Context, logger *zap.Logger, pageID int64) ([]orm.Topologic, error) {
func QueryTopologicByPageID(ctx context.Context, tx *gorm.DB, logger *zap.Logger, pageID int64) ([]orm.Topologic, error) {
var topologics []orm.Topologic
// ctx超时判断
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
result := _globalPostgresClient.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Raw(sql.RecursiveSQL, pageID).Scan(&topologics)
result := tx.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Raw(sql.RecursiveSQL, pageID).Scan(&topologics)
if result.Error != nil {
logger.Error("query circuit diagram topologic info by pageID failed", zap.Int64("pageID", pageID), zap.Error(result.Error))
return nil, result.Error
@ -29,19 +31,20 @@ func QueryTopologicByPageID(ctx context.Context, logger *zap.Logger, pageID int6
}
// QueryTopologicFromDB return the result of query topologic info from postgresDB
func QueryTopologicFromDB(ctx context.Context, logger *zap.Logger, gridID, zoneID, stationID int64) error {
allPages, err := QueryAllPages(ctx, logger, gridID, zoneID, stationID)
func QueryTopologicFromDB(ctx context.Context, tx *gorm.DB, logger *zap.Logger, gridID, zoneID, stationID int64) error {
allPages, err := QueryAllPages(ctx, tx, logger, gridID, zoneID, stationID)
if err != nil {
logger.Error("query all pages info failed", zap.Int64("gridID", gridID), zap.Int64("zoneID", zoneID), zap.Int64("stationID", stationID), zap.Error(err))
return err
}
for _, page := range allPages {
topologicInfos, err := QueryTopologicByPageID(ctx, logger, page.ID)
topologicInfos, err := QueryTopologicByPageID(ctx, tx, logger, page.ID)
if err != nil {
logger.Error("query topologic info by pageID failed", zap.Int64("pageID", page.ID), zap.Error(err))
return err
}
err = InitCircuitDiagramTopologic(page.ID, topologicInfos)
if err != nil {
logger.Error("init topologic failed", zap.Error(err))

View File

@ -4,6 +4,7 @@ package database
import (
"context"
"fmt"
"strconv"
"time"
"modelRT/constant"
@ -15,39 +16,47 @@ import (
)
// UpdateComponentIntoDB define update component info of the circuit diagram into DB
func UpdateComponentIntoDB(ctx context.Context, tx *gorm.DB, componentInfos []network.ComponentUpdateInfo) error {
func UpdateComponentIntoDB(ctx context.Context, tx *gorm.DB, componentInfo network.ComponentUpdateInfo) (int64, error) {
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
for _, info := range componentInfos {
globalUUID, err := uuid.FromString(info.UUID)
if err != nil {
return fmt.Errorf("format uuid from string type failed:%w", err)
}
componentInfo := orm.Component{
GlobalUUID: globalUUID,
GridID: info.GridID,
ZoneID: info.ZoneID,
StationID: info.StationID,
ComponentType: info.ComponentType,
State: info.State,
ConnectedBus: info.ConnectedBus,
Name: info.Name,
VisibleID: info.Name,
Description: info.Description,
Context: info.Context,
Comment: info.Comment,
InService: info.InService,
}
result := tx.Model(&orm.Component{}).WithContext(cancelCtx).Updates(&componentInfo)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check update component conditions", constant.ErrUpdateRowZero)
}
return fmt.Errorf("update component info failed:%w", err)
}
globalUUID, err := uuid.FromString(componentInfo.UUID)
if err != nil {
return -1, fmt.Errorf("format uuid from string type failed:%w", err)
}
return nil
var component orm.Component
result := tx.Model(&orm.Component{}).WithContext(cancelCtx).Where("global_uuid = ?", globalUUID).Find(&component)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check update component conditions", constant.ErrUpdateRowZero)
}
return -1, fmt.Errorf("query component info failed:%w", err)
}
updateParams := orm.Component{
GlobalUUID: globalUUID,
GridID: strconv.FormatInt(componentInfo.GridID, 10),
ZoneID: strconv.FormatInt(componentInfo.ZoneID, 10),
StationID: strconv.FormatInt(componentInfo.StationID, 10),
PageID: componentInfo.PageID,
Tag: componentInfo.Tag,
ComponentType: componentInfo.ComponentType,
Name: componentInfo.Name,
Context: componentInfo.Context,
Op: componentInfo.Op,
Ts: time.Now(),
}
result = tx.Model(&orm.Component{}).WithContext(cancelCtx).Where("id = ?", component.ID).Updates(&updateParams)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check update component conditions", constant.ErrUpdateRowZero)
}
return -1, fmt.Errorf("update component info failed:%w", err)
}
return component.ID, nil
}

View File

@ -8,43 +8,34 @@ import (
"modelRT/constant"
"modelRT/model"
"modelRT/network"
"github.com/gofrs/uuid"
jsoniter "github.com/json-iterator/go"
"gorm.io/gorm"
)
// UpdateModelIntoDB define update component model params of the circuit diagram into DB
func UpdateModelIntoDB(ctx context.Context, tx *gorm.DB, componentInfos []network.ComponentUpdateInfo) error {
func UpdateModelIntoDB(ctx context.Context, tx *gorm.DB, componentID int64, componentType int, modelParas string) error {
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
for _, componentInfo := range componentInfos {
modelStruct := model.SelectModelByType(componentInfo.ComponentType)
if modelStruct == nil {
return fmt.Errorf("can not get component model by model type %d", componentInfo.ComponentType)
}
modelStruct := model.SelectModelByType(componentType)
if modelStruct == nil {
return fmt.Errorf("can not get component model by model type %d", componentType)
}
err := jsoniter.Unmarshal([]byte(componentInfo.Params), modelStruct)
if err != nil {
return fmt.Errorf("unmarshal component info by component struct %s,failed", model.SelectModelNameByType(componentInfo.ComponentType))
}
err := jsoniter.Unmarshal([]byte(modelParas), modelStruct)
if err != nil {
return fmt.Errorf("unmarshal component info by component struct %s,failed", model.SelectModelNameByType(componentType))
}
modelStruct.SetComponentID(componentID)
globalUUID, err := uuid.FromString(componentInfo.UUID)
if err != nil {
return fmt.Errorf("format uuid from string type failed:%w", err)
}
modelStruct.SetUUID(globalUUID)
result := tx.Model(modelStruct).WithContext(cancelCtx).Where("uuid = ?", componentInfo.UUID).Updates(modelStruct)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check where conditions", constant.ErrUpdateRowZero)
}
return err
result := tx.Model(modelStruct).WithContext(cancelCtx).Where("component_id = ?", componentID).Updates(modelStruct)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check where conditions", constant.ErrUpdateRowZero)
}
return err
}
return nil
}

View File

@ -24,13 +24,33 @@ func UpdateTopologicIntoDB(ctx context.Context, tx *gorm.DB, pageID int64, chang
case constant.UUIDFromChangeType:
result = tx.WithContext(cancelCtx).Model(&orm.Topologic{}).Where("page_id = ? and uuid_from = ? and uuid_to = ?", pageID, changeInfo.OldUUIDFrom, changeInfo.OldUUIDTo).Updates(orm.Topologic{UUIDFrom: changeInfo.NewUUIDFrom})
case constant.UUIDToChangeType:
var delTopologic orm.Topologic
result = tx.WithContext(cancelCtx).Model(&orm.Topologic{}).Where("page_id = ? and uuid_to = ?", pageID, changeInfo.NewUUIDTo).Find(&delTopologic)
if result.Error != nil {
return fmt.Errorf("find topologic link by new_uuid_to failed:%w", result.Error)
}
if result.RowsAffected == 1 {
// delete old topologic link
result = tx.WithContext(cancelCtx).Where("id = ?", delTopologic.ID).Delete(&delTopologic)
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check delete topologic where conditions", constant.ErrDeleteRowZero)
}
return fmt.Errorf("del old topologic link by new_uuid_to failed:%w", err)
}
}
result = tx.WithContext(cancelCtx).Model(&orm.Topologic{}).Where("page_id = ? and uuid_from = ? and uuid_to = ?", pageID, changeInfo.OldUUIDFrom, changeInfo.OldUUIDTo).Updates(&orm.Topologic{UUIDTo: changeInfo.NewUUIDTo})
case constant.UUIDAddChangeType:
topologic := orm.Topologic{
PageID: pageID,
Flag: changeInfo.Flag,
UUIDFrom: changeInfo.NewUUIDFrom,
UUIDTo: changeInfo.OldUUIDFrom,
UUIDTo: changeInfo.NewUUIDTo,
Comment: changeInfo.Comment,
}
result = tx.WithContext(cancelCtx).Create(&topologic)

41
diagram/anchor_set.go Normal file
View File

@ -0,0 +1,41 @@
package diagram
import (
"errors"
"fmt"
"sync"
)
// anchorValueOverview define struct of storage all anchor value
var anchorValueOverview sync.Map
// GetAnchorValue define func of get circuit diagram data by componentID
func GetAnchorValue(componentID int64) (string, error) {
value, ok := diagramsOverview.Load(componentID)
if !ok {
return "", fmt.Errorf("can not find anchor value by componentID:%d", componentID)
}
anchorValue, ok := value.(string)
if !ok {
return "", errors.New("convert to string failed")
}
return anchorValue, nil
}
// UpdateAnchorValue define func of update anchor value by componentID and anchor name
func UpdateAnchorValue(componentID int64, anchorValue string) bool {
_, result := anchorValueOverview.Swap(componentID, anchorValue)
return result
}
// StoreAnchorValue define func of store anchor value with componentID and anchor name
func StoreAnchorValue(componentID int64, anchorValue string) {
anchorValueOverview.Store(componentID, anchorValue)
return
}
// DeleteAnchorValue define func of delete anchor value with componentID
func DeleteAnchorValue(componentID int64) {
anchorValueOverview.Delete(componentID)
return
}

View File

@ -1,43 +0,0 @@
package diagram
import (
"errors"
"fmt"
"sync"
cmap "github.com/orcaman/concurrent-map/v2"
)
// DiagramsOverview define struct of storage all circuit diagram data
var diagramsOverview sync.Map
// GetComponentMap define func of get circuit diagram data by global uuid
func GetComponentMap(uuid string) (*cmap.ConcurrentMap[string, any], error) {
value, ok := diagramsOverview.Load(uuid)
if !ok {
return nil, fmt.Errorf("can not find graph by global uuid:%s", uuid)
}
paramsMap, ok := value.(*cmap.ConcurrentMap[string, any])
if !ok {
return nil, errors.New("convert to component map struct failed")
}
return paramsMap, nil
}
// UpdateComponentMap define func of update circuit diagram data by global uuid and component info
func UpdateComponentMap(uuid string, componentInfo *cmap.ConcurrentMap[string, any]) bool {
_, result := diagramsOverview.Swap(uuid, componentInfo)
return result
}
// StoreComponentMap define func of store circuit diagram data with global uuid and component info
func StoreComponentMap(uuid string, componentInfo *cmap.ConcurrentMap[string, any]) {
diagramsOverview.Store(uuid, componentInfo)
return
}
// DeleteComponentMap define func of delete circuit diagram data with global uuid
func DeleteComponentMap(uuid string) {
diagramsOverview.Delete(uuid)
return
}

41
diagram/component_set.go Normal file
View File

@ -0,0 +1,41 @@
package diagram
import (
"errors"
"fmt"
"sync"
)
// diagramsOverview define struct of storage all circuit diagram data
var diagramsOverview sync.Map
// GetComponentMap define func of get circuit diagram data by component id
func GetComponentMap(componentID int64) (map[string]interface{}, error) {
value, ok := diagramsOverview.Load(componentID)
if !ok {
return nil, fmt.Errorf("can not find graph by global uuid:%d", componentID)
}
paramsMap, ok := value.(map[string]interface{})
if !ok {
return nil, errors.New("convert to component map struct failed")
}
return paramsMap, nil
}
// UpdateComponentMap define func of update circuit diagram data by component id and component info
func UpdateComponentMap(componentID int64, componentInfo map[string]interface{}) bool {
_, result := diagramsOverview.Swap(componentID, componentInfo)
return result
}
// StoreComponentMap define func of store circuit diagram data with component id and component info
func StoreComponentMap(componentID int64, componentInfo map[string]interface{}) {
diagramsOverview.Store(componentID, componentInfo)
return
}
// DeleteComponentMap define func of delete circuit diagram data with component id
func DeleteComponentMap(componentID int64) {
diagramsOverview.Delete(componentID)
return
}

View File

@ -105,12 +105,14 @@ func (g *Graph) DelEdge(from, to uuid.UUID) error {
fromKeys = []uuid.UUID{from}
}
// Process the situation where the to node is taken as the parent node while deleting edges
childvertex := g.VerticeLinks[toKey]
err := g.DelNode(toKey)
if err != nil {
return fmt.Errorf("delete edge failed: %w", err)
}
fmt.Println("fromKeys:", fromKeys)
for _, fromUUID := range fromKeys {
fromKey := fromUUID.String()
var delIndex int
@ -119,10 +121,15 @@ func (g *Graph) DelEdge(from, to uuid.UUID) error {
delIndex = index
}
}
vertex := g.VerticeLinks[fromKey]
copy(vertex[delIndex:], vertex[delIndex+1:])
vertex = vertex[:len(vertex)-1]
g.VerticeLinks[fromKey] = vertex
if len(vertex) == 1 {
g.DelNode(fromKey)
} else {
copy(vertex[delIndex:], vertex[delIndex+1:])
vertex = vertex[:len(vertex)-1]
g.VerticeLinks[fromKey] = vertex
}
}
fromKey := from.String()

View File

@ -6,9 +6,17 @@ import (
"sync"
)
// GraphOverview define struct of storage all circuit diagram topologic data
// graphOverview define struct of storage all circuit diagram topologic data
var graphOverview sync.Map
// PrintGrapMap define func of print circuit diagram topologic info data
func PrintGrapMap() {
graphOverview.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true
})
}
// GetGraphMap define func of get circuit diagram topologic data by pageID
func GetGraphMap(pageID int64) (*Graph, error) {
value, ok := graphOverview.Load(pageID)
@ -24,18 +32,18 @@ func GetGraphMap(pageID int64) (*Graph, error) {
// UpdateGrapMap define func of update circuit diagram data by pageID and topologic info
func UpdateGrapMap(pageID int64, graphInfo *Graph) bool {
_, result := diagramsOverview.Swap(pageID, graphInfo)
_, result := graphOverview.Swap(pageID, graphInfo)
return result
}
// StoreGraphMap define func of store circuit diagram topologic data with pageID and topologic info
func StoreGraphMap(pageID int64, graphInfo *Graph) {
diagramsOverview.Store(pageID, graphInfo)
graphOverview.Store(pageID, graphInfo)
return
}
// DeleteGraphMap define func of delete circuit diagram topologic data with pageID
func DeleteGraphMap(pageID int64) {
diagramsOverview.Delete(pageID)
graphOverview.Delete(pageID)
return
}

View File

@ -0,0 +1,13 @@
{
"params_list": [{
"anchor_name": "voltage",
"func_type": "1",
"upper_limit": 23,
"lower_limit": 0.5
}, {
"anchor_name": "current",
"func_type": "2",
"upper_limit": 23,
"lower_limit": 0.5
}]
}

View File

@ -1,52 +1,37 @@
{
"page_id":1,
"topologics":[
"page_id": 1,
"topologics": [
{
"uuid_from":"12311-111",
"uuid_to":"12311-114"
"uuid_from": "70c190f2-8a60-42a9-b143-ec5f87e0aa6b",
"uuid_to": "70c190f2-8a75-42a9-b166-ec5f87e0aa6b"
},
{
"uuid_from":"12311-115",
"uuid_to":"12311-116"
"uuid_from": "70c190f2-8a75-42a9-b166-ec5f87e0aa6b",
"uuid_to": "70c200f2-8a75-42a9-c166-bf5f87e0aa6b"
}
],
"free_vertexs":[
"12311-111",
"12311-112",
"12311-115"
],
"component_infos":[
"component_infos": [
{
"grid_id":1,
"zone":1,
"station_id":1,
"uuid":"12311-114",
"name":"母线 1",
"component_type":1,
"in_service":true,
"connected_bus":1,
"state":1,
"visible_id":"",
"description":"",
"context":"",
"comment":"",
"params":""
"component_type": 3,
"grid_id": 1,
"name": "demo21",
"page_id": 1,
"station_id": 1,
"uuid": "70c190f2-8a75-42a9-b166-ec5f87e0aa6b",
"zone_id": 1,
"context":"{\"top\":\"7.4\",\"left\":\"3.2\",\"right\":\"3.5\",\"buttom\":\"1.2\"}",
"params": "{\"anchor_i\":false,\"anchor_v\":true,\"context\":\"{\\\"top\\\": \\\"7.4\\\", \\\"left\\\": \\\"3.2\\\", \\\"right\\\": \\\"3.5\\\", \\\"buttom\\\": \\\"1.2\\\"}\",\"name\":\"demo21\",\"oi_alarm\":110,\"op\":1,\"ov_alarm\":110,\"resistance\":100,\"ts\":\"2021-01-01T08:00:00+08:00\",\"ui_alarm\":90,\"uv_alarm\":90}"
},
{
"grid_id":1,
"zone":1,
"station_id":1,
"uuid":"12311-116",
"name":"发电机 1",
"component_type":1,
"in_service":true,
"connected_bus":1,
"state":1,
"visible_id":"",
"description":"",
"context":"",
"comment":"",
"params":""
"component_type": 3,
"grid_id": 1,
"name": "demo22",
"page_id": 1,
"station_id": 1,
"uuid": "70c200f2-8a75-42a9-c166-bf5f87e0aa6b",
"zone_id": 1,
"context":"{\"top\":\"7.4\",\"left\":\"3.2\",\"right\":\"3.5\",\"buttom\":\"1.2\"}",
"params": "{\"anchor_i\":false,\"anchor_v\":true,\"context\":\"{\\\"top\\\": \\\"7.4\\\", \\\"left\\\": \\\"3.2\\\", \\\"right\\\": \\\"3.5\\\", \\\"buttom\\\": \\\"1.2\\\"}\",\"name\":\"demo22\",\"oi_alarm\":110,\"op\":1,\"ov_alarm\":110,\"resistance\":100,\"ts\":\"2021-01-01T08:00:00+08:00\",\"ui_alarm\":90,\"uv_alarm\":90}"
}
]
}

View File

@ -2,27 +2,22 @@
"page_id":1,
"topologics":[
{
"uuid_from":"12311-111",
"uuid_to":"12311-114"
"uuid_from": "70c190f2-8a75-42a9-b166-ec5f87e0aa6b",
"uuid_to": "70c200f2-8a75-42a9-c166-bf5f87e0aa6b"
},
{
"uuid_from":"12311-115",
"uuid_to":"12311-116"
"uuid_from": "70c190f2-8a60-42a9-b143-ec5f87e0aa6b",
"uuid_to": "70c190f2-8a75-42a9-b166-ec5f87e0aa6b"
}
],
"free_vertexs":[
"12311-111",
"12311-112",
"12311-115"
],
"component_infos":[
{
"uuid":"12311-114",
"component_type":1
"uuid":"70c200f2-8a75-42a9-c166-bf5f87e0aa6b",
"component_type":3
},
{
"uuid":"12311-116",
"component_type":1
"uuid":"70c190f2-8a75-42a9-b166-ec5f87e0aa6b",
"component_type":3
}
]
}

View File

@ -3,63 +3,37 @@
"topologics":[
{
"change_type":1,
"old_uuid_from":"12311-111",
"old_uuid_to":"12311-113",
"new_uuid_from":"12311-111",
"new_uuid_to":"12311-114"
"old_uuid_from":"e32bc0be-67f4-4d79-a5da-eaa40a5bd77d",
"old_uuid_to":"70c200f2-8a75-42a9-c166-bf5f87e0aa6b",
"new_uuid_from":"70c190f2-8a75-42a9-b166-ec5f87e0aa6b",
"new_uuid_to":"70c200f2-8a75-42a9-c166-bf5f87e0aa6b"
},
{
"change_type":2,
"old_uuid_from":"12311-111",
"old_uuid_to":"12311-113",
"new_uuid_from":"12311-112",
"new_uuid_to":"12311-113"
"old_uuid_from":"70c190f2-8a60-42a9-b143-ec5f87e0aa6b",
"old_uuid_to":"10f155cf-bd27-4557-85b2-d126b6e2657f",
"new_uuid_from":"70c190f2-8a60-42a9-b143-ec5f87e0aa6b",
"new_uuid_to":"70c200f2-8a75-42a9-c166-bf5f87e0aa6b"
},
{
"change_type":3,
"old_uuid_from":"",
"old_uuid_to":"",
"new_uuid_from":"12311-115",
"new_uuid_to":"12311-116"
"new_uuid_from":"70c200f2-8a75-42a9-c166-bf5f87e0aa6b",
"new_uuid_to":"10f155cf-bd27-4557-85b2-d126b6e2657f"
}
],
"free_vertexs":[
"12311-111",
"12311-112",
"12311-115"
],
"component_infos":[
"component_infos": [
{
"grid_id":1,
"zone":1,
"station_id":1,
"uuid":"12311-114",
"name":"母线 1",
"component_type":1,
"in_service":true,
"connected_bus":1,
"state":1,
"visible_id":"",
"description":"",
"context":"",
"comment":"",
"params":""
},
{
"grid_id":1,
"zone":1,
"station_id":1,
"uuid":"12311-116",
"name":"发电机 1",
"component_type":1,
"in_service":true,
"connected_bus":1,
"state":1,
"visible_id":"",
"description":"",
"context":"",
"comment":"",
"params":""
"component_type": 3,
"grid_id": 1,
"name": "demo23",
"page_id": 1,
"station_id": 1,
"uuid": "70c200f2-8a75-42a9-c166-bf5f87e0aa6b",
"zone_id": 1,
"context":"{\"top\":\"7.4\",\"left\":\"3.2\",\"right\":\"3.5\",\"buttom\":\"1.2\"}",
"params": "{\"anchor_i\":false,\"anchor_v\":true,\"context\":\"{\\\"top\\\": \\\"7.4\\\", \\\"left\\\": \\\"3.2\\\", \\\"right\\\": \\\"3.5\\\", \\\"buttom\\\": \\\"1.2\\\"}\",\"name\":\"demo23\",\"oi_alarm\":110,\"op\":1,\"ov_alarm\":110,\"resistance\":100,\"ts\":\"2021-01-01T08:00:00+08:00\",\"ui_alarm\":90,\"uv_alarm\":90}"
}
]
}

18
go.mod
View File

@ -3,14 +3,18 @@ module modelRT
go 1.22.5
require (
github.com/bitly/go-simplejson v0.5.1
github.com/confluentinc/confluent-kafka-go v1.9.2
github.com/gin-gonic/gin v1.10.0
github.com/gofrs/uuid v4.4.0+incompatible
github.com/gorilla/websocket v1.5.3
github.com/json-iterator/go v1.1.12
github.com/natefinch/lumberjack v2.0.0+incompatible
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/panjf2000/ants/v2 v2.10.0
github.com/spf13/viper v1.19.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.4
go.uber.org/zap v1.27.0
gorm.io/driver/postgres v1.5.9
gorm.io/gorm v1.25.12
@ -19,13 +23,10 @@ require (
require (
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.2.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/bytedance/sonic v1.12.5 // indirect
github.com/bytedance/sonic/loader v0.2.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.7 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
@ -54,22 +55,15 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/swaggo/gin-swagger v1.6.0 // indirect
github.com/swaggo/swag v1.16.4 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/urfave/cli/v2 v2.27.5 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/arch v0.12.0 // indirect
golang.org/x/crypto v0.30.0 // indirect
@ -82,7 +76,5 @@ require (
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

65
go.sum
View File

@ -5,19 +5,14 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PuerkitoBio/purell v1.2.1 h1:QsZ4TjvwiMpat6gBCBxEQI0rcS9ehtkKtSpiUnd9N28=
github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/actgardner/gogen-avro/v10 v10.1.0/go.mod h1:o+ybmVjEa27AAr35FRqU98DJu1fXES56uXniYFv4yDA=
github.com/actgardner/gogen-avro/v10 v10.2.1/go.mod h1:QUhjeHPchheYmMDni/Nx7VB0RsT/ee8YIgGY/xpEQgQ=
github.com/actgardner/gogen-avro/v9 v9.1.0/go.mod h1:nyTj6wPqDJoxM3qdnjcLv+EnMDSDFqE0qDpva2QRmKc=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
github.com/bytedance/sonic v1.12.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w=
github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
@ -39,8 +34,6 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/confluentinc/confluent-kafka-go v1.9.2 h1:gV/GxhMBUb03tFWkN+7kdhg+zf+QUM+wVkI9zwh770Q=
github.com/confluentinc/confluent-kafka-go v1.9.2/go.mod h1:ptXNqsuDfYbAE/LBW6pnwWZElUoWxHoV8E43DCrliyo=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -61,11 +54,11 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
@ -84,12 +77,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
@ -120,12 +109,14 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hamba/avro v1.5.6/go.mod h1:3vNT0RLXXpFm2Tb/5KC71ZRJlOroggq1Rcitb6k4Fr8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@ -159,8 +150,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/juju/qthttptest v0.1.1/go.mod h1:aTlAv8TYaflIiTDIQYzxnl1QdPjAg8Q8qJMErpKy6A4=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
@ -197,12 +186,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/nrwiersma/avro-benchmarks v0.0.0-20210913175520-21aec48c8f76/go.mod h1:iKyFMidsk/sVYONJRE372sJuX/QTRPacU7imPqqsu7g=
github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@ -214,17 +199,13 @@ github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a/go.mod h1:4r5QyqhjI
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
@ -238,7 +219,6 @@ github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@ -247,7 +227,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
@ -262,10 +241,6 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@ -275,17 +250,12 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -296,6 +266,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -312,8 +284,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -326,8 +296,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -345,8 +313,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -358,8 +324,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -404,8 +368,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/avro.v0 v0.0.0-20171217001914-a730b5802183/go.mod h1:FvqrFXt+jCsyQibeRv4xxEJBL5iG2DDW5aeJwzDiq4A=
@ -440,6 +402,3 @@ gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@ -0,0 +1,46 @@
// Package handler provides HTTP handlers for various endpoints.
package handler
import (
"net/http"
"strconv"
"modelRT/alert"
"modelRT/constant"
"modelRT/logger"
"modelRT/network"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// QueryAlertEventHandler define query alert event process API
func QueryAlertEventHandler(c *gin.Context) {
var targetLevel constant.AlertLevel
logger := logger.GetLoggerInstance()
alertManger := alert.GetAlertMangerInstance()
levelStr := c.Query("level")
level, err := strconv.Atoi(levelStr)
if err != nil {
logger.Error("convert alert level string to int failed", zap.Error(err))
resp := network.FailureResponse{
Code: -1,
Msg: err.Error(),
}
c.JSON(http.StatusOK, resp)
}
targetLevel = constant.AlertLevel(level)
events := alertManger.GetRangeEventsByLevel(targetLevel)
resp := network.SuccessResponse{
Code: 0,
Msg: "success",
PayLoad: map[string]interface{}{
"events": events,
},
}
c.JSON(http.StatusOK, resp)
}

View File

@ -0,0 +1,112 @@
// Package handler provides HTTP handlers for various endpoints.
package handler
import (
"context"
"fmt"
"net/http"
"time"
"modelRT/constant"
"modelRT/database"
"modelRT/diagram"
"modelRT/logger"
"modelRT/model"
"modelRT/network"
"modelRT/orm"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// ComponentAnchorReplaceHandler define component anchor point replace process API
func ComponentAnchorReplaceHandler(c *gin.Context) {
var uuid, anchorName string
logger := logger.GetLoggerInstance()
pgClient := database.GetPostgresDBClient()
cancelCtx, cancel := context.WithTimeout(c, 5*time.Second)
defer cancel()
var request network.ComponetAnchorReplaceRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("unmarshal component anchor point replace info failed", zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
}
c.JSON(http.StatusOK, resp)
return
}
uuid = request.UUID
anchorName = request.AnchorName
var componentInfo orm.Component
result := pgClient.WithContext(cancelCtx).Model(&orm.Component{}).Where("global_uuid = ?", uuid).Find(&componentInfo)
if result.Error != nil {
logger.Error("query component detail info failed", zap.Error(result.Error))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: result.Error.Error(),
}
c.JSON(http.StatusOK, resp)
return
}
if result.RowsAffected == 0 {
err := fmt.Errorf("query component detail info by uuid failed:%w", constant.ErrQueryRowZero)
logger.Error("query component detail info from table is empty", zap.String("table_name", "component"))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
}
c.JSON(http.StatusOK, resp)
return
}
cancelCtx, cancel = context.WithTimeout(c, 5*time.Second)
defer cancel()
unmarshalMap := make(map[string]interface{})
tableName := model.SelectModelNameByType(componentInfo.ComponentType)
result = pgClient.WithContext(cancelCtx).Table(tableName).Where("global_uuid = ?", uuid).Find(&unmarshalMap)
if result.Error != nil {
logger.Error("query model detail info failed", zap.Error(result.Error))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: result.Error.Error(),
}
c.JSON(http.StatusOK, resp)
return
}
if unmarshalMap == nil {
err := fmt.Errorf("query model detail info by uuid failed:%w", constant.ErrQueryRowZero)
logger.Error("query model detail info from table is empty", zap.String("table_name", tableName))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
}
c.JSON(http.StatusOK, resp)
return
}
componentType := unmarshalMap["component_type"].(int)
if componentType != constant.DemoType {
logger.Error("can not process real time data of component type not equal DemoType", zap.Int64("component_id", componentInfo.ID))
}
diagram.UpdateAnchorValue(componentInfo.ID, anchorName)
resp := network.SuccessResponse{
Code: http.StatusOK,
Msg: "success",
PayLoad: map[string]interface{}{
"uuid": request.UUID,
},
}
c.JSON(http.StatusOK, resp)
}

View File

@ -1,3 +1,4 @@
// Package handler provides HTTP handlers for various endpoints.
package handler
import (
@ -6,26 +7,27 @@ import (
"modelRT/database"
"modelRT/diagram"
"modelRT/log"
"modelRT/logger"
"modelRT/network"
"github.com/bitly/go-simplejson"
"github.com/gin-gonic/gin"
"github.com/gofrs/uuid"
cmap "github.com/orcaman/concurrent-map/v2"
"go.uber.org/zap"
)
// CircuitDiagramCreateHandler define circuit diagram create process API
func CircuitDiagramCreateHandler(c *gin.Context) {
logger := log.GetLoggerInstance()
logger := logger.GetLoggerInstance()
pgClient := database.GetPostgresDBClient()
var request network.CircuitDiagramCreateRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("unmarshal circuit diagram create info failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
}
c.JSON(http.StatusOK, resp)
return
@ -34,9 +36,10 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
graph, err := diagram.GetGraphMap(request.PageID)
if err != nil {
logger.Error("get topologic data from set by pageID failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"page_id": request.PageID,
},
@ -61,9 +64,10 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
}
logger.Error("format uuid from string failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"topologic_info": topologicLink,
},
@ -84,9 +88,10 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
tx.Rollback()
logger.Error("create topologic info into DB failed", zap.Any("topologic_info", topologicCreateInfos), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"topologic_infos": topologicCreateInfos,
},
@ -99,45 +104,54 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
graph.AddEdge(topologicCreateInfo.UUIDFrom, topologicCreateInfo.UUIDTo)
}
err = database.CreateComponentIntoDB(c, tx, request.ComponentInfos)
if err != nil {
tx.Rollback()
for index, componentInfo := range request.ComponentInfos {
componentID, err := database.CreateComponentIntoDB(c, tx, componentInfo)
if err != nil {
tx.Rollback()
logger.Error("insert component info into DB failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
PayLoad: map[string]interface{}{
"component_infos": request.ComponentInfos,
},
}
c.JSON(http.StatusOK, resp)
return
}
logger.Error("insert component info into DB failed", zap.Error(err))
err = database.CreateModelIntoDB(c, tx, request.ComponentInfos)
if err != nil {
logger.Error("create component model into DB failed", zap.Any("component_infos", request.ComponentInfos), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
PayLoad: map[string]interface{}{
"uuid": request.PageID,
"component_infos": request.ComponentInfos,
},
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"component_infos": request.ComponentInfos,
},
}
c.JSON(http.StatusOK, resp)
return
}
request.ComponentInfos[index].ID = componentID
err = database.CreateModelIntoDB(c, tx, componentID, componentInfo.ComponentType, componentInfo.Params)
if err != nil {
tx.Rollback()
logger.Error("create component model into DB failed", zap.Any("component_infos", request.ComponentInfos), zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": request.PageID,
"component_infos": request.ComponentInfos,
},
}
c.JSON(http.StatusOK, resp)
return
}
c.JSON(http.StatusOK, resp)
return
}
for _, componentInfo := range request.ComponentInfos {
componentMap := cmap.New[any]()
err = componentMap.UnmarshalJSON([]byte(componentInfo.Params))
paramsJSON, err := simplejson.NewJson([]byte(componentInfo.Params))
if err != nil {
logger.Error("unmarshal component info by concurrent map failed", zap.String("component_params", componentInfo.Params), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
tx.Rollback()
logger.Error("unmarshal component params info failed", zap.String("component_params", componentInfo.Params), zap.Error(err))
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
"component_params": componentInfo.Params,
@ -146,7 +160,25 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
c.JSON(http.StatusOK, resp)
return
}
diagram.StoreComponentMap(componentInfo.UUID, &componentMap)
componentMap, err := paramsJSON.Map()
if err != nil {
tx.Rollback()
logger.Error("format params json info to map failed", zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
"component_params": componentInfo.Params,
},
}
c.JSON(http.StatusOK, resp)
return
}
diagram.StoreComponentMap(componentInfo.ID, componentMap)
}
if len(request.FreeVertexs) > 0 {
@ -155,10 +187,11 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
}
}
// commit transsction
// commit transaction
tx.Commit()
resp := network.SuccessResponse{
SuccessResponseHeader: network.SuccessResponseHeader{Status: http.StatusOK},
Code: http.StatusOK,
Msg: "success",
PayLoad: map[string]interface{}{
"page_id": request.PageID,
},

View File

@ -1,3 +1,4 @@
// Package handler provides HTTP handlers for various endpoints.
package handler
import (
@ -9,7 +10,7 @@ import (
"modelRT/constant"
"modelRT/database"
"modelRT/diagram"
"modelRT/log"
"modelRT/logger"
"modelRT/model"
"modelRT/network"
"modelRT/orm"
@ -17,19 +18,21 @@ import (
"github.com/gin-gonic/gin"
"github.com/gofrs/uuid"
"go.uber.org/zap"
"gorm.io/gorm/clause"
)
// CircuitDiagramDeleteHandler define circuit diagram delete process API
func CircuitDiagramDeleteHandler(c *gin.Context) {
logger := log.GetLoggerInstance()
logger := logger.GetLoggerInstance()
pgClient := database.GetPostgresDBClient()
var request network.CircuitDiagramDeleteRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("unmarshal circuit diagram del info failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
}
c.JSON(http.StatusOK, resp)
return
@ -38,9 +41,10 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
graph, err := diagram.GetGraphMap(request.PageID)
if err != nil {
logger.Error("get topologic data from set by pageID failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"page_id": request.PageID,
},
@ -65,9 +69,10 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
}
logger.Error("format uuid from string failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"topologic_info": topologicLink,
},
@ -89,9 +94,10 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
tx.Rollback()
logger.Error("delete topologic info into DB failed", zap.Any("topologic_info", topologicDelInfo), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"topologic_info": topologicDelInfo,
},
@ -105,9 +111,10 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
tx.Rollback()
logger.Error("delete topologic info failed", zap.Any("topologic_info", topologicDelInfo), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"topologic_info": topologicDelInfo,
},
@ -130,9 +137,10 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
tx.Rollback()
logger.Error("format uuid from string failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
},
@ -142,8 +150,30 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
}
component := &orm.Component{GlobalUUID: globalUUID}
result := tx.WithContext(cancelCtx).Delete(component)
var component orm.Component
result := tx.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Where("global_uuid = ?", globalUUID).Find(&component)
if result.Error != nil || result.RowsAffected == 0 {
tx.Rollback()
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check uuid conditions", constant.ErrDeleteRowZero)
}
logger.Error("query component info into postgresDB failed", zap.String("component_global_uuid", componentInfo.UUID), zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
},
}
c.JSON(http.StatusOK, resp)
return
}
result = tx.WithContext(cancelCtx).Delete(component)
if result.Error != nil || result.RowsAffected == 0 {
tx.Rollback()
@ -153,9 +183,10 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
}
logger.Error("delete component info into postgresDB failed", zap.String("component_global_uuid", componentInfo.UUID), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
},
@ -165,8 +196,8 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
}
modelStruct := model.SelectModelByType(component.ComponentType)
modelStruct.SetUUID(globalUUID)
result = tx.WithContext(cancelCtx).Delete(modelStruct)
modelStruct.SetComponentID(component.ID)
result = tx.WithContext(cancelCtx).Where("component_id = ?", component.ID).Delete(modelStruct)
if result.Error != nil || result.RowsAffected == 0 {
tx.Rollback()
@ -177,9 +208,10 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
msg := fmt.Sprintf("delete component info from table %s failed", modelStruct.ReturnTableName())
logger.Error(msg, zap.String("component_global_uuid", componentInfo.UUID), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
},
@ -187,7 +219,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
c.JSON(http.StatusOK, resp)
return
}
diagram.DeleteComponentMap(componentInfo.UUID)
diagram.DeleteComponentMap(component.ID)
}
if len(request.FreeVertexs) > 0 {
@ -196,10 +228,11 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
}
}
// commit transsction
// commit transaction
tx.Commit()
resp := network.SuccessResponse{
SuccessResponseHeader: network.SuccessResponseHeader{Status: http.StatusOK},
Code: http.StatusOK,
Msg: "success",
PayLoad: map[string]interface{}{
"page_id": request.PageID,
},

View File

@ -1,11 +1,13 @@
// Package handler provides HTTP handlers for various endpoints.
package handler
import (
"net/http"
"strconv"
"modelRT/database"
"modelRT/diagram"
"modelRT/log"
"modelRT/logger"
"modelRT/network"
"github.com/gin-gonic/gin"
@ -23,13 +25,16 @@ import (
// @Failure 400 {object} network.FailureResponse "request process failed"
// @Router /model/diagram_load/{page_id} [get]
func CircuitDiagramLoadHandler(c *gin.Context) {
logger := log.GetLoggerInstance()
logger := logger.GetLoggerInstance()
pgClient := database.GetPostgresDBClient()
pageID, err := strconv.ParseInt(c.Query("page_id"), 10, 64)
if err != nil {
logger.Error("get pageID from url param failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"page_id": pageID,
},
@ -41,9 +46,10 @@ func CircuitDiagramLoadHandler(c *gin.Context) {
topologicInfo, err := diagram.GetGraphMap(pageID)
if err != nil {
logger.Error("get topologic data from set by pageID failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"page_id": pageID,
},
@ -55,43 +61,79 @@ func CircuitDiagramLoadHandler(c *gin.Context) {
payLoad["root_vertex"] = topologicInfo.RootVertex
payLoad["topologic"] = topologicInfo.VerticeLinks
componentParamMap := make(map[string][]byte)
componentParamMap := make(map[string]any)
for _, VerticeLink := range topologicInfo.VerticeLinks {
for _, componentUUID := range VerticeLink {
UUIDStr := componentUUID.String()
componentParams, err := diagram.GetComponentMap(UUIDStr)
component, err := database.QueryComponentByUUID(c, pgClient, componentUUID)
if err != nil {
logger.Error("get component data from set by uuid failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
logger.Error("get component id info from DB by uuid failed", zap.Error(err))
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": UUIDStr,
"uuid": componentUUID,
},
}
c.JSON(http.StatusOK, resp)
return
}
byteSlice, err := componentParams.MarshalJSON()
componentParams, err := diagram.GetComponentMap(component.ID)
if err != nil {
logger.Error("marshal component data failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
logger.Error("get component data from set by uuid failed", zap.Error(err))
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentUUID,
},
}
c.JSON(http.StatusOK, resp)
return
}
componentParamMap[UUIDStr] = byteSlice
componentParamMap[componentUUID.String()] = componentParams
}
}
rootVertexUUID := topologicInfo.RootVertex.String()
rootComponent, err := database.QueryComponentByUUID(c, pgClient, topologicInfo.RootVertex)
if err != nil {
logger.Error("get component id info from DB by uuid failed", zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": topologicInfo.RootVertex,
},
}
c.JSON(http.StatusOK, resp)
return
}
rootComponentParam, err := diagram.GetComponentMap(rootComponent.ID)
if err != nil {
logger.Error("get component data from set by uuid failed", zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": rootVertexUUID,
},
}
c.JSON(http.StatusOK, resp)
return
}
componentParamMap[rootVertexUUID] = rootComponentParam
payLoad["component_params"] = componentParamMap
resp := network.SuccessResponse{
SuccessResponseHeader: network.SuccessResponseHeader{
Status: http.StatusOK,
ErrMsg: "",
},
Code: http.StatusOK,
Msg: "success",
PayLoad: payLoad,
}
c.JSON(http.StatusOK, resp)

View File

@ -1,3 +1,4 @@
// Package handler provides HTTP handlers for various endpoints.
package handler
import (
@ -5,25 +6,26 @@ import (
"modelRT/database"
"modelRT/diagram"
"modelRT/log"
"modelRT/logger"
"modelRT/network"
"github.com/bitly/go-simplejson"
"github.com/gin-gonic/gin"
cmap "github.com/orcaman/concurrent-map/v2"
"go.uber.org/zap"
)
// CircuitDiagramUpdateHandler define circuit diagram update process API
func CircuitDiagramUpdateHandler(c *gin.Context) {
logger := log.GetLoggerInstance()
logger := logger.GetLoggerInstance()
pgClient := database.GetPostgresDBClient()
var request network.CircuitDiagramUpdateRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("unmarshal circuit diagram update info failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
}
c.JSON(http.StatusOK, resp)
return
@ -32,9 +34,10 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
graph, err := diagram.GetGraphMap(request.PageID)
if err != nil {
logger.Error("get topologic data from set by pageID failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"page_id": request.PageID,
},
@ -48,9 +51,10 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
changeInfo, err := network.ParseUUID(topologicLink)
if err != nil {
logger.Error("format uuid from string failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"topologic_info": topologicLink,
},
@ -70,9 +74,10 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
tx.Rollback()
logger.Error("update topologic info into DB failed", zap.Any("topologic_info", topologicChangeInfo), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"topologic_info": topologicChangeInfo,
},
@ -86,9 +91,10 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
tx.Rollback()
logger.Error("update topologic info failed", zap.Any("topologic_info", topologicChangeInfo), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"topologic_info": topologicChangeInfo,
},
@ -98,44 +104,50 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
}
}
err = database.UpdateComponentIntoDB(c, tx, request.ComponentInfos)
if err != nil {
logger.Error("udpate component info into DB failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
PayLoad: map[string]interface{}{
"page_id": request.PageID,
"component_info": request.ComponentInfos,
},
}
c.JSON(http.StatusOK, resp)
return
}
for index, componentInfo := range request.ComponentInfos {
componentID, err := database.UpdateComponentIntoDB(c, tx, componentInfo)
if err != nil {
logger.Error("udpate component info into DB failed", zap.Error(err))
err = database.UpdateModelIntoDB(c, tx, request.ComponentInfos)
if err != nil {
logger.Error("udpate component model info into DB failed", zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
PayLoad: map[string]interface{}{
"page_id": request.PageID,
"component_info": request.ComponentInfos,
},
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"page_id": request.PageID,
"component_info": request.ComponentInfos,
},
}
c.JSON(http.StatusOK, resp)
return
}
request.ComponentInfos[index].ID = componentID
err = database.UpdateModelIntoDB(c, tx, componentID, componentInfo.ComponentType, componentInfo.Params)
if err != nil {
logger.Error("udpate component model info into DB failed", zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"page_id": request.PageID,
"component_info": request.ComponentInfos,
},
}
c.JSON(http.StatusOK, resp)
return
}
c.JSON(http.StatusOK, resp)
return
}
for _, componentInfo := range request.ComponentInfos {
componentMap := cmap.New[any]()
err = componentMap.UnmarshalJSON([]byte(componentInfo.Params))
paramsJSON, err := simplejson.NewJson([]byte(componentInfo.Params))
if err != nil {
logger.Error("unmarshal component info by concurrent map failed", zap.String("component_params", componentInfo.Params), zap.Error(err))
header := network.FailResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.FailureResponse{
FailResponseHeader: header,
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
"component_params": componentInfo.Params,
@ -144,7 +156,23 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
c.JSON(http.StatusOK, resp)
return
}
diagram.UpdateComponentMap(componentInfo.UUID, &componentMap)
componentMap, err := paramsJSON.Map()
if err != nil {
logger.Error("format params json info to map failed", zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
"component_params": componentInfo.Params,
},
}
c.JSON(http.StatusOK, resp)
return
}
diagram.UpdateComponentMap(componentInfo.ID, componentMap)
}
if len(request.FreeVertexs) > 0 {
@ -153,11 +181,12 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
}
}
// commit transsction
// commit transaction
tx.Commit()
resp := network.SuccessResponse{
SuccessResponseHeader: network.SuccessResponseHeader{Status: http.StatusOK},
Code: http.StatusOK,
Msg: "success",
PayLoad: map[string]interface{}{
"page_id": request.PageID,
},

View File

@ -0,0 +1,46 @@
// Package handler provides HTTP handlers for various endpoints.
package handler
import (
"net/http"
"strconv"
"modelRT/alert"
"modelRT/constant"
"modelRT/logger"
"modelRT/network"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// QueryRealTimeDataHandler define query real time data process API
func QueryRealTimeDataHandler(c *gin.Context) {
var targetLevel constant.AlertLevel
logger := logger.GetLoggerInstance()
alertManger := alert.GetAlertMangerInstance()
levelStr := c.Query("level")
level, err := strconv.Atoi(levelStr)
if err != nil {
logger.Error("convert alert level string to int failed", zap.Error(err))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
}
c.JSON(http.StatusOK, resp)
}
targetLevel = constant.AlertLevel(level)
events := alertManger.GetRangeEventsByLevel(targetLevel)
resp := network.SuccessResponse{
Code: http.StatusOK,
Msg: "success",
PayLoad: map[string]interface{}{
"events": events,
},
}
c.JSON(http.StatusOK, resp)
}

View File

@ -0,0 +1,102 @@
package handler
import (
"modelRT/logger"
"modelRT/network"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
jsoniter "github.com/json-iterator/go"
"go.uber.org/zap"
realtimedata "modelRT/real-time-data"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
// RealTimeDataReceivehandler define real time data receive and process API
func RealTimeDataReceivehandler(c *gin.Context) {
logger := logger.GetLoggerInstance()
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
logger.Error("upgrade http protocol to websocket protocal failed", zap.Error(err))
return
}
defer conn.Close()
for {
messageType, p, err := conn.ReadMessage()
if err != nil {
logger.Error("read message from websocket connection failed", zap.Error(err))
respByte := processResponse(-1, "read message from websocket connection failed", nil)
if len(respByte) == 0 {
logger.Error("process message from byte failed", zap.Error(err))
continue
}
err = conn.WriteMessage(messageType, respByte)
if err != nil {
logger.Error("write message to websocket connection failed", zap.Error(err))
continue
}
continue
}
var request network.RealTimeDataReceiveRequest
err = jsoniter.Unmarshal([]byte(p), &request)
if err != nil {
logger.Error("unmarshal message from byte failed", zap.Error(err))
respByte := processResponse(-1, "unmarshal message from byte failed", nil)
if len(respByte) == 0 {
logger.Error("process message from byte failed", zap.Error(err))
continue
}
err = conn.WriteMessage(messageType, respByte)
if err != nil {
logger.Error("write message to websocket connection failed", zap.Error(err))
continue
}
continue
}
realtimedata.RealTimeDataChan <- request
payload := map[string]interface{}{
"component_id": request.PayLoad.ComponentID,
"point": request.PayLoad.Point,
}
respByte := processResponse(0, "success", payload)
if len(respByte) == 0 {
logger.Error("process message from byte failed", zap.Error(err))
continue
}
err = conn.WriteMessage(messageType, respByte)
if err != nil {
logger.Error("write message to websocket connection failed", zap.Error(err))
continue
}
}
}
func processResponse(code int64, msg string, payload map[string]interface{}) []byte {
resp := map[string]interface{}{
"code": code,
"msg": msg,
"payload": payload,
}
respByte, err := jsoniter.Marshal(resp)
if err != nil {
return []byte("")
}
return respByte
}

View File

@ -1,5 +1,5 @@
// Package log define log struct of wave record project
package log
// Package logger define log struct of modelRT project
package logger
import (
"os"

73
main.go
View File

@ -6,18 +6,20 @@ import (
"flag"
"time"
"modelRT/alert"
"modelRT/config"
"modelRT/database"
_ "modelRT/docs"
"modelRT/handler"
"modelRT/log"
"modelRT/logger"
"modelRT/middleware"
"modelRT/pool"
subscription "modelRT/real-time-data"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
realtimedata "modelRT/real-time-data"
"github.com/gin-gonic/gin"
"github.com/panjf2000/ants/v2"
"go.uber.org/zap"
@ -39,7 +41,8 @@ var (
var (
modelRTConfig config.ModelRTConfig
postgresDBClient *gorm.DB
logger *zap.Logger
zapLogger *zap.Logger
alertManager *alert.EventManager
)
// TODO 使用 wire 依赖注入管理 DVIE 面板注册的 panel
@ -60,34 +63,54 @@ func main() {
}()
// init logger
logger = log.InitLoggerInstance(modelRTConfig.LoggerConfig)
defer logger.Sync()
zapLogger = logger.InitLoggerInstance(modelRTConfig.LoggerConfig)
defer zapLogger.Sync()
// init ants pool
pool, err := ants.NewPoolWithFunc(modelRTConfig.ParseConcurrentQuantity, pool.ParseFunc)
// init alert manager
_ = alert.InitAlertEventManager()
// init model parse ants pool
parsePool, err := ants.NewPoolWithFunc(modelRTConfig.ParseConcurrentQuantity, pool.ParseFunc)
if err != nil {
logger.Error("init concurrent parse task pool failed", zap.Error(err))
zapLogger.Error("init concurrent parse task pool failed", zap.Error(err))
panic(err)
}
defer ants.Release()
defer parsePool.Release()
// load circuit diagram from postgres
err = database.QueryCircuitDiagramComponentFromDB(ctx, pool, logger)
// init anchor param ants pool
anchorRealTimePool, err := pool.AnchorPoolInit(modelRTConfig.RTDReceiveConcurrentQuantity)
if err != nil {
logger.Error("load circuit diagrams from postgres failed", zap.Error(err))
zapLogger.Error("init concurrent anchor param task pool failed", zap.Error(err))
panic(err)
}
defer anchorRealTimePool.Release()
// TODO 暂时屏蔽完成 swagger 启动测试
err = database.QueryTopologicFromDB(ctx, logger, modelRTConfig.GridID, modelRTConfig.ZoneID, modelRTConfig.StationID)
if err != nil {
logger.Error("load topologic info from postgres failed", zap.Error(err))
panic(err)
}
// init cancel context
cancelCtx, cancel := context.WithCancel(ctx)
defer cancel()
// init real time data receive channel
go realtimedata.ReceiveChan(cancelCtx)
postgresDBClient.Transaction(func(tx *gorm.DB) error {
// load circuit diagram from postgres
err := database.QueryCircuitDiagramComponentFromDB(cancelCtx, tx, parsePool, zapLogger)
if err != nil {
zapLogger.Error("load circuit diagrams from postgres failed", zap.Error(err))
panic(err)
}
// TODO 暂时屏蔽完成 swagger 启动测试
err = database.QueryTopologicFromDB(ctx, tx, zapLogger, modelRTConfig.GridID, modelRTConfig.ZoneID, modelRTConfig.StationID)
if err != nil {
zapLogger.Error("load topologic info from postgres failed", zap.Error(err))
panic(err)
}
return nil
})
// TODO 完成订阅数据分析
// TODO 暂时屏蔽完成 swagger 启动测试
go subscription.RealTimeDataComputer(ctx, nil, []string{}, "")
// go realtimedata.RealTimeDataComputer(ctx, nil, []string{}, "")
engine := gin.Default()
engine.Use(limiter.Middleware)
@ -98,6 +121,18 @@ func main() {
engine.POST("/model/diagram_update", handler.CircuitDiagramUpdateHandler)
engine.POST("/model/diagram_delete", handler.CircuitDiagramDeleteHandler)
// real time data api
engine.GET("/ws/rtdatas", handler.RealTimeDataReceivehandler)
// anchor api
engine.POST("/model/anchor_replace", handler.ComponentAnchorReplaceHandler)
// alert api
engine.GET("/alert/events/query", handler.QueryAlertEventHandler)
// real time data api
engine.GET("/rt/datas/query", handler.QueryRealTimeDataHandler)
// dashborad api
dashboard := engine.Group("/dashboard", limiter.Middleware)
{

View File

@ -1,10 +1,8 @@
// Package model define model struct of model runtime service
package model
import "github.com/gofrs/uuid"
// BasicModelInterface define basic model type interface
type BasicModelInterface interface {
SetUUID(uuid uuid.UUID)
SetComponentID(componentID int64)
ReturnTableName() string
}

View File

@ -12,6 +12,8 @@ func SelectModelByType(modelType int) BasicModelInterface {
return &orm.BusbarSection{}
} else if modelType == constant.AsyncMotorType {
return &orm.AsyncMotor{}
} else if modelType == constant.DemoType {
return &orm.Demo{}
}
return nil
}

View File

@ -0,0 +1,8 @@
// Package network define struct of network operation
package network
// ComponetAnchorReplaceRequest defines request params of component anchor point replace api
type ComponetAnchorReplaceRequest struct {
UUID string `json:"uuid"`
AnchorName string `json:"anchor_name"`
}

84
network/api_endpoint.go Normal file
View File

@ -0,0 +1,84 @@
// Package network define struct of network operation
package network
import (
"fmt"
"io"
"net/http"
"strings"
"github.com/bitly/go-simplejson"
)
// APIEndpoint defines an api endpoint struct to poll data from dataRT service
type APIEndpoint struct {
URL string `json:"url"`
Method string `json:"method"` // HTTP 方法,如 "GET", "POST"
Headers map[string]string `json:"headers"`
QueryParams map[string]string `json:"query_params"`
Body string `json:"body"` // 对于 POST 请求需要一个请求体
Interval int `json:"interval"` // 轮询间隔时间(秒)
}
// fetchAPI defines execute http request and return response or error
func fetchAPI(endpoint APIEndpoint) (string, error) {
client := &http.Client{}
req, err := http.NewRequest(endpoint.Method, endpoint.URL, nil)
if err != nil {
return "", err
}
for key, value := range endpoint.Headers {
req.Header.Set(key, value)
}
query := req.URL.Query()
for key, value := range endpoint.QueryParams {
query.Set(key, value)
}
req.URL.RawQuery = query.Encode()
if endpoint.Method == "POST" || endpoint.Method == "PUT" {
req.Body = io.NopCloser(strings.NewReader(endpoint.Body))
req.Header.Set("Content-Type", "application/json")
}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
// PollAPIEndpoints defines unmarshal polling data from http request
func PollAPIEndpoints(endpoint APIEndpoint) ([]float64, error) {
var valueSlice []float64
respStr, err := fetchAPI(endpoint)
if err != nil {
return valueSlice, fmt.Errorf("fetch api failed:%w", err)
}
realDataJSON, err := simplejson.NewJson([]byte(respStr))
if err != nil {
return valueSlice, fmt.Errorf("format real time data failed:%w", err)
}
simplejson.New().UnmarshalJSON([]byte(respStr))
code := realDataJSON.Get("code").MustInt()
if code != 0 {
return valueSlice, fmt.Errorf("polling data api status error:%s", realDataJSON.Get("msg").MustString())
}
dataLen := len(realDataJSON.Get("data").MustArray())
for i := 0; i < dataLen; i++ {
valueSlice = append(valueSlice, realDataJSON.Get("data").GetIndex(i).Get("value").MustFloat64())
}
return valueSlice, nil
}

View File

@ -21,20 +21,18 @@ type TopologicUUIDCreateInfo struct {
// ComponentCreateInfo defines circuit diagram component create index info
type ComponentCreateInfo struct {
ID int64 `json:"id"`
UUID string `json:"uuid"`
Name string `json:"name"`
VisibleID string `json:"visible_id"`
Description string `json:"description"`
Context string `json:"context"`
Comment string `json:"comment"`
Params string `json:"params"`
GridID int64 `json:"grid_id"`
ZoneID int64 `json:"zone_id"`
StationID int64 `json:"station_id"`
PageID int64 `json:"page_id"`
Tag string `json:"tag"`
ComponentType int `json:"component_type"`
State int `json:"state"`
ConnectedBus int `json:"connected_bus"`
InService bool `json:"in_service"`
Params string `json:"params"`
Op int `json:"op"`
}
// CircuitDiagramCreateRequest defines request params of circuit diagram create api

View File

@ -33,20 +33,18 @@ type TopologicUUIDChangeInfos struct {
// ComponentUpdateInfo defines circuit diagram component params info
type ComponentUpdateInfo struct {
ID int64 `json:"id"`
UUID string `json:"uuid"`
Name string `json:"name"`
VisibleID string `json:"visible_id"`
Description string `json:"description"`
Context string `json:"context"`
Comment string `json:"comment"`
Params string `json:"params"`
GridID int64 `json:"grid_id"`
ZoneID int64 `json:"zone_id"`
StationID int64 `json:"station_id"`
PageID int64 `json:"page_id"`
ComponentType int `json:"component_type"`
State int `json:"state"`
ConnectedBus int `json:"connected_bus"`
InService bool `json:"in_service"`
Params string `json:"params"`
Op int `json:"op"`
Tag string `json:"tag"`
}
// CircuitDiagramUpdateRequest defines request params of circuit diagram update api
@ -60,6 +58,7 @@ type CircuitDiagramUpdateRequest struct {
// ParseUUID define parse UUID by change type in topologic change struct
func ParseUUID(info TopologicChangeInfo) (TopologicUUIDChangeInfos, error) {
var UUIDChangeInfo TopologicUUIDChangeInfos
UUIDChangeInfo.ChangeType = info.ChangeType
switch info.ChangeType {
case constant.UUIDFromChangeType:

View File

@ -0,0 +1,20 @@
// Package network define struct of network operation
package network
// RealTimeDataReceiveRequest defines request params of real time data receive api
type RealTimeDataReceiveRequest struct {
PayLoad RealTimeDataReceivePayload `json:"payload"`
}
// RealTimeDataReceivePayload defines request payload of real time data receive api
type RealTimeDataReceivePayload struct {
ComponentID int64 `json:"component_id"`
Point string `json:"point"`
Values []RealTimeDataReceiveParam `json:"values"`
}
// RealTimeDataReceiveParam defines request param of real time data receive api
type RealTimeDataReceiveParam struct {
Time int64 `json:"time"`
Value float64 `json:"value"`
}

View File

@ -1,22 +1,14 @@
// Package network define struct of network operation
package network
type SuccessResponseHeader struct {
Status int `json:"status" example:"200"`
ErrMsg string `json:"err_msg"`
}
type FailResponseHeader struct {
Status int `json:"status" example:"400"`
ErrMsg string `json:"err_msg"`
}
type FailureResponse struct {
FailResponseHeader `json:"header"`
PayLoad map[string]interface{} `json:"payload" swaggertype:"object,string" example:"key:value"`
Code int `json:"code" example:"200"`
Msg string `json:"msg"`
PayLoad map[string]interface{} `json:"payload" swaggertype:"object,string" example:"key:value"`
}
type SuccessResponse struct {
SuccessResponseHeader `json:"header"`
PayLoad map[string]interface{} `json:"payload" swaggertype:"object,string" example:"key:value"`
Code int `json:"code" example:"200"`
Msg string `json:"msg"`
PayLoad map[string]interface{} `json:"payload" swaggertype:"object,string" example:"key:value"`
}

View File

@ -6,6 +6,7 @@ import "github.com/gofrs/uuid"
// AsyncMotor define asynchronous motor model
type AsyncMotor struct {
UUID uuid.UUID `gorm:"column:global_uuid;primaryKey"`
ComponentID int64 // compoent表ID
NoLoadVoltage float64 // 空载电压
NoLoadElectricCurrent float64 // 空载电流
NoLoadPower float64 // 空载电功率
@ -81,9 +82,9 @@ func (a *AsyncMotor) TableName() string {
return "AsyncMotor"
}
// SetUUID func implement BasicModelInterface interface
func (a *AsyncMotor) SetUUID(uuid uuid.UUID) {
a.UUID = uuid
// SetComponentID func implement BasicModelInterface interface
func (a *AsyncMotor) SetComponentID(componentID int64) {
a.ComponentID = componentID
return
}

View File

@ -6,6 +6,7 @@ import "github.com/gofrs/uuid"
// BusbarSection define busbar section model
type BusbarSection struct {
// 母线基本参数
ComponentID int64 // compoent表ID
BusbarName string // 母线端名称,默认值BusX
BusbarNumber int // 母线编号,默认值1
UUID uuid.UUID `gorm:"column:global_uuid;primaryKey"`
@ -68,9 +69,9 @@ func (b *BusbarSection) TableName() string {
return "BusbarSection"
}
// SetUUID func implement BasicModelInterface interface
func (b *BusbarSection) SetUUID(uuid uuid.UUID) {
b.UUID = uuid
// SetComponentID func implement BasicModelInterface interface
func (b *BusbarSection) SetComponentID(componentID int64) {
b.ComponentID = componentID
return
}

View File

@ -2,28 +2,28 @@
package orm
import (
"time"
"github.com/gofrs/uuid"
)
// Component structure define abstracted info set of electrical component
type Component struct {
ID int64 `gorm:"column:id"`
GlobalUUID uuid.UUID `gorm:"column:global_uuid;primaryKey"`
GridID int64 `gorm:"column:grid"`
ZoneID int64 `gorm:"column:zone"`
StationID int64 `gorm:"column:station"`
ID int64 `gorm:"column:id;primaryKey"`
GlobalUUID uuid.UUID `gorm:"column:global_uuid"`
Tag string `gorm:"column:tag"`
GridID string `gorm:"column:grid"`
ZoneID string `gorm:"column:zone"`
StationID string `gorm:"column:station"`
PageID int64 `gorm:"column:page_id"`
ComponentType int `gorm:"column:type"`
State int `gorm:"column:state"`
ConnectedBus int `gorm:"column:connected_bus"`
Name string `gorm:"column:name"`
VisibleID string `gorm:"column:visible_id"`
Description string `gorm:"column:description"`
Context string `gorm:"column:context"`
Comment string `gorm:"column:comment"`
InService bool `gorm:"column:in_service"`
Op int `gorm:"column:op"`
Ts time.Time `gorm:"column:ts"`
}
// TableName func respresent return table name of Component
func (c *Component) TableName() string {
return "Component"
return "component"
}

View File

@ -5,17 +5,18 @@ import "time"
// Page structure define circuit diagram page info set
type Page struct {
ID int64 `gorm:"column:id"`
StationID int64 `gorm:"column:station_id"`
Status int `gorm:"column:status"`
Name string `gorm:"column:name"`
Owner string `gorm:"column:owner"`
Comment string `gorm:"column:comment"`
Context string `gorm:"column:context"`
TSModified time.Time `gorm:"column:ts_modified"`
ID int64 `gorm:"column:id;primaryKey"`
Status int `gorm:"column:status"`
Op int `gorm:"column:op"`
Label string `gorm:"column:label"`
Context string `gorm:"column:context"`
Description string `gorm:"column:description"`
Tag string `gorm:"column:tag"`
Name string `gorm:"column:name"`
Ts time.Time `gorm:"column:ts"`
}
// TableName func respresent return table name of Page
func (p *Page) TableName() string {
return "Page"
return "page"
}

34
orm/demo.go Normal file
View File

@ -0,0 +1,34 @@
// Package orm define database data struct
package orm
import "time"
type Demo struct {
ID int64 `gorm:"column:id" json:"id"` // 主键 ID
ComponentID int64 `gorm:"column:component_id" json:"component_id"` // compoent表ID
Resistance float32 `gorm:"column:resistance" json:"resistance"` // 电阻值
AnchorV bool `gorm:"column:anchor_v" json:"anchor_v"` // 是否锚定电压
UVAlarm float32 `gorm:"column:uv_alarm" json:"uv_alarm"` // 欠压告警值
OVAlarm float32 `gorm:"column:ov_alarm" json:"ov_alarm"` // 过压告警值
AnchorI bool `gorm:"column:anchor_i" json:"anchor_i"` // 是否锚定电流
UIAlarm float32 `gorm:"column:ui_alarm" json:"ui_alarm"` // 低电流告警值
OIAlarm float32 `gorm:"column:oi_alarm" json:"oi_alarm"` // 高电流告警值
Op int `gorm:"column:op" json:"op"` // 操作人 ID
Ts time.Time `gorm:"column:ts" json:"ts"` // 操作时间
}
// TableName func respresent return table name of busbar section
func (d *Demo) TableName() string {
return "bus_stability"
}
// SetComponentID func implement BasicModelInterface interface
func (d *Demo) SetComponentID(componentID int64) {
d.ComponentID = componentID
return
}
// ReturnTableName func implement BasicModelInterface interface
func (d *Demo) ReturnTableName() string {
return "bus_stability"
}

View File

@ -0,0 +1,56 @@
package pool
import (
"context"
"sync"
"modelRT/config"
)
func init() {
globalComponentChanSet = &ComponentChanSet{
AnchorChans: make([]chan config.AnchorParamConfig, 100),
}
}
var globalComponentChanSet *ComponentChanSet
// ComponentChanSet defines component anchor real time data process channel set
type ComponentChanSet struct {
sync.RWMutex
AnchorChans []chan config.AnchorParamConfig
}
func GetComponentChan(ctx context.Context, componentID int64) chan config.AnchorParamConfig {
globalComponentChanSet.RLock()
componentChan := globalComponentChanSet.AnchorChans[componentID]
if componentChan == nil {
globalComponentChanSet.RUnlock()
globalComponentChanSet.Lock()
defer globalComponentChanSet.Unlock()
return CreateNewComponentChan(ctx, componentID)
}
globalComponentChanSet.RUnlock()
return componentChan
}
func CreateNewComponentChan(ctx context.Context, componentID int64) chan config.AnchorParamConfig {
componentChan := globalComponentChanSet.AnchorChans[componentID]
if componentChan == nil {
componentChan = make(chan config.AnchorParamConfig, 100)
globalComponentChanSet.AnchorChans[componentID] = componentChan
readyChan := make(chan struct{})
chanConfig := config.AnchorChanConfig{
Ctx: ctx,
AnchorChan: componentChan,
ReadyChan: readyChan,
}
AnchorRealTimePool.Invoke(chanConfig)
<-readyChan
return componentChan
}
return componentChan
}

View File

@ -0,0 +1,87 @@
// Package pool define concurrency call function in ants
package pool
import (
"fmt"
"time"
"modelRT/alert"
"modelRT/config"
"modelRT/constant"
"modelRT/diagram"
"modelRT/logger"
"github.com/panjf2000/ants/v2"
"go.uber.org/zap"
)
// AnchorRealTimePool define anchor param pool of real time data
var AnchorRealTimePool *ants.PoolWithFunc
// AnchorPoolInit define anchor param pool init
func AnchorPoolInit(concurrentQuantity int) (pool *ants.PoolWithFunc, err error) {
// init anchor param ants pool
AnchorRealTimePool, err = ants.NewPoolWithFunc(concurrentQuantity, AnchorFunc)
if err != nil {
return nil, err
}
return AnchorRealTimePool, nil
}
// AnchorFunc defines func that process the real time data of component anchor params
var AnchorFunc = func(poolConfig interface{}) {
var firstStart bool
logger := logger.GetLoggerInstance()
alertManager := alert.GetAlertMangerInstance()
anchorChanConfig, ok := poolConfig.(config.AnchorChanConfig)
if !ok {
logger.Error("conversion component anchor chan type failed")
return
}
for {
select {
case <-anchorChanConfig.Ctx.Done():
return
case anchorParaConfig := <-anchorChanConfig.AnchorChan:
if firstStart {
close(anchorChanConfig.ReadyChan)
firstStart = false
}
componentID := anchorParaConfig.ComponentID
anchorRealTimeDatas := anchorParaConfig.AnchorRealTimeData
for _, value := range anchorRealTimeDatas {
anchorName, err := diagram.GetAnchorValue(componentID)
if err != nil {
logger.Error("can not get anchor value from map by uuid", zap.Int64("component_id", componentID), zap.Error(err))
continue
}
if anchorName != anchorParaConfig.AnchorName {
logger.Error("anchor name not equal param config anchor value", zap.String("map_anchor_name", anchorName), zap.String("param_anchor_name", anchorParaConfig.AnchorName))
continue
}
upperLimitVal := anchorParaConfig.CompareValUpperLimit
lowerlimitVal := anchorParaConfig.CompareValLowerLimit
compareValue := anchorParaConfig.CalculateFunc(value, anchorParaConfig.CalculateParams...)
if compareValue > upperLimitVal || compareValue < lowerlimitVal {
message := fmt.Sprintf("anchor param %s value out of range, value:%f, upper limit:%f, lower limit:%f", anchorName,
compareValue, upperLimitVal, lowerlimitVal)
event := alert.Event{
ComponentID: componentID,
AnchorName: anchorName,
Level: constant.InfoAlertLevel,
Message: message,
StartTime: time.Now().Unix(),
}
alertManager.AddEvent(event)
}
}
default:
}
}
}

View File

@ -3,55 +3,67 @@ package pool
import (
"context"
"errors"
"time"
"modelRT/config"
"modelRT/database"
"modelRT/diagram"
"modelRT/logger"
"modelRT/model"
cmap "github.com/orcaman/concurrent-map/v2"
"go.uber.org/zap"
)
// ParseFunc defines func that parses the model data from postgres
var ParseFunc = func(parseConfig interface{}) {
logger := zap.L()
logger := logger.GetLoggerInstance()
modelParseConfig, ok := parseConfig.(config.ModelParseConfig)
if !ok {
logger.Error("conversion model parse config type failed")
panic(errors.New("conversion model parse config type failed"))
return
}
cancelCtx, cancel := context.WithTimeout(modelParseConfig.Context, 5*time.Second)
defer cancel()
pgClient := database.GetPostgresDBClient()
uuid := modelParseConfig.ComponentInfo.GlobalUUID.String()
unmarshalMap := cmap.New[any]()
unmarshalMap := make(map[string]interface{})
tableName := model.SelectModelNameByType(modelParseConfig.ComponentInfo.ComponentType)
result := pgClient.Table(tableName).WithContext(cancelCtx).Find(&unmarshalMap)
result := pgClient.WithContext(cancelCtx).Table(tableName).Where("component_id = ?", modelParseConfig.ComponentInfo.ID).Find(&unmarshalMap)
if result.Error != nil {
logger.Error("query component detail info failed", zap.Error(result.Error))
return
} else if result.RowsAffected == 0 {
logger.Error("query component detail info from table is empty", zap.String("table_name", tableName))
return
}
unmarshalMap.Set("id", modelParseConfig.ComponentInfo.ID)
unmarshalMap.Set("uuid", modelParseConfig.ComponentInfo.GlobalUUID.String())
unmarshalMap.Set("created_time", modelParseConfig.ComponentInfo.VisibleID)
unmarshalMap.Set("parent_id", modelParseConfig.ComponentInfo.GridID)
unmarshalMap.Set("type", modelParseConfig.ComponentInfo.ZoneID)
unmarshalMap.Set("created_time", modelParseConfig.ComponentInfo.StationID)
unmarshalMap.Set("updated_time", modelParseConfig.ComponentInfo.ComponentType)
unmarshalMap.Set("id", modelParseConfig.ComponentInfo.State)
unmarshalMap.Set("parent_id", modelParseConfig.ComponentInfo.ConnectedBus)
unmarshalMap.Set("type", modelParseConfig.ComponentInfo.Name)
unmarshalMap.Set("updated_time", modelParseConfig.ComponentInfo.Description)
unmarshalMap.Set("id", modelParseConfig.ComponentInfo.Context)
unmarshalMap.Set("parent_id", modelParseConfig.ComponentInfo.Comment)
unmarshalMap.Set("type", modelParseConfig.ComponentInfo.InService)
diagram.StoreComponentMap(uuid, &unmarshalMap)
var anchorName string
if anchoringV := unmarshalMap["anchor_v"].(bool); anchoringV {
anchorName = "voltage"
} else {
anchorName = "current"
}
diagram.StoreAnchorValue(modelParseConfig.ComponentInfo.ID, anchorName)
GetComponentChan(modelParseConfig.Context, modelParseConfig.ComponentInfo.ID)
uuid := modelParseConfig.ComponentInfo.GlobalUUID.String()
unmarshalMap["id"] = modelParseConfig.ComponentInfo.ID
unmarshalMap["uuid"] = uuid
unmarshalMap["uuid"] = modelParseConfig.ComponentInfo.Tag
unmarshalMap["grid_id"] = modelParseConfig.ComponentInfo.GridID
unmarshalMap["zone_id"] = modelParseConfig.ComponentInfo.ZoneID
unmarshalMap["station_id"] = modelParseConfig.ComponentInfo.StationID
unmarshalMap["page_id"] = modelParseConfig.ComponentInfo.PageID
unmarshalMap["component_type"] = modelParseConfig.ComponentInfo.ComponentType
unmarshalMap["name"] = modelParseConfig.ComponentInfo.Name
unmarshalMap["context"] = modelParseConfig.ComponentInfo.Context
unmarshalMap["op"] = modelParseConfig.ComponentInfo.Op
unmarshalMap["ts"] = modelParseConfig.ComponentInfo.Ts
diagram.StoreComponentMap(modelParseConfig.ComponentInfo.ID, unmarshalMap)
return
}

143
real-time-data/cache.go Normal file
View File

@ -0,0 +1,143 @@
// Package realtimedata define real time data operation functions
package realtimedata
import (
"container/heap"
"fmt"
"sync"
"time"
)
// DataItem define structure for storing data, insertion time, and last access time
type DataItem struct {
Data interface{}
InsertTime time.Time
LastAccess time.Time
Index int
}
// priorityQueueItem implement heap.Interface
type priorityQueueItem struct {
item *DataItem
prio int64
}
// priorityQueue implement heap.Interface
type priorityQueue []*priorityQueueItem
func (pq priorityQueue) Len() int { return len(pq) }
func (pq priorityQueue) Less(i, j int) bool {
// Note: use the UnixNano value of LastAccess time, with smaller values indicating earlier. Therefore, we need to perform reverse comparison to implement LRU like behavior
return pq[i].prio < pq[j].prio
}
func (pq priorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].item.Index = i
pq[j].item.Index = j
}
func (pq *priorityQueue) Push(x interface{}) {
n := len(*pq)
queueItem := x.(*priorityQueueItem)
queueItem.item.Index = n
*pq = append(*pq, queueItem)
}
func (pq *priorityQueue) Pop() interface{} {
old := *pq
n := len(old)
queueItem := old[n-1]
queueItem.item.Index = -1
*pq = old[0 : n-1]
return queueItem
}
// update updates the priority of an item in the priority queue.
func (pq *priorityQueue) update(item *DataItem, newPrio int64) {
item.LastAccess = time.Unix(0, newPrio)
pqItem := (*pq)[item.Index]
pqItem.prio = newPrio
heap.Fix(pq, item.Index)
}
type TimeCache struct {
mu sync.Mutex
capacity int
items map[interface{}]*DataItem
pq priorityQueue
}
// NewTimeCache 创建一个新的 TimeCache 实例
func NewTimeCache(capacity int) *TimeCache {
return &TimeCache{
capacity: capacity,
items: make(map[interface{}]*DataItem),
pq: make(priorityQueue, 0, capacity),
}
}
// Add 添加一个新项到缓存中
func (tc *TimeCache) Add(data interface{}) {
tc.mu.Lock()
defer tc.mu.Unlock()
now := time.Now()
// 如果数据已经存在,则更新它的最后访问时间
if existingItem, ok := tc.items[data]; ok {
prio := existingItem.LastAccess.UnixNano()
tc.pq.update(existingItem, prio+1) // 实际上我们不需要 +1但这里为了演示如何更新优先级
// 注意:上面的更新操作是多余的,因为我们总是可以重新插入一个新项并删除旧项
// 因此,下面的代码将替换上面的更新操作
tc.pq.Remove(tc.pq.search(existingItem))
delete(tc.items, data)
}
newItem := &DataItem{
Data: data,
InsertTime: now,
LastAccess: now,
Index: -1, // 初始时索引无效
}
pqItem := &priorityQueueItem{
item: newItem,
prio: newItem.LastAccess.UnixNano(),
}
heap.Push(&tc.pq, pqItem)
newItem.Index = pqItem.item.Index
tc.items[data] = newItem
// 如果缓存超过了容量,移除最后访问时间最早的项
if tc.pq.Len() > tc.capacity {
oldestItem := heap.Pop(&tc.pq).(*priorityQueueItem).item
delete(tc.items, oldestItem.Data)
}
}
// search 辅助函数,用于在优先级队列中搜索一个项
func (pq *priorityQueue) search(item *DataItem) int {
for i, pqItem := range *pq {
if pqItem.item == item {
return i
}
}
return -1 // 未找到
}
// Remove 辅助函数,用于从优先级队列中移除一个项(注意:这不是 heap.Interface 的一部分)
func (pq *priorityQueue) Remove(index int) {
pqItem := (*pq)[index]
pqItem.item.Index = -1 // 将索引设为无效值
*pq = append((*pq)[:index], (*pq)[index+1:]...)
heap.Fix(pq, index)
}
func (tc *TimeCache) PrintCache() {
tc.mu.Lock()
defer tc.mu.Unlock()
for _, item := range tc.items {
fmt.Printf("Data: %v, InsertTime: %s, LastAccess: %s\n", item.Data, item.InsertTime, item.LastAccess)
}
}

View File

@ -1,11 +1,11 @@
// Package subscription define real time data operation functions
package subscription
// Package realtimedata define real time data operation functions
package realtimedata
import (
"context"
"time"
"modelRT/log"
"modelRT/logger"
"github.com/confluentinc/confluent-kafka-go/kafka"
"go.uber.org/zap"
@ -18,9 +18,10 @@ func RealTimeDataComputer(ctx context.Context, consumerConfig kafka.ConfigMap, t
defer cancel()
// get a logger
logger := log.GetLoggerInstance()
logger := logger.GetLoggerInstance()
// setup a channel to listen for interrupt signals
// TODO 将中断信号放到入参中
interrupt := make(chan struct{}, 1)
// read message (-1 means wait indefinitely)

View File

@ -0,0 +1,86 @@
// Package realtimedata define real time data operation functions
package realtimedata
import (
"context"
"modelRT/config"
"modelRT/constant"
"modelRT/diagram"
"modelRT/logger"
"modelRT/network"
"modelRT/pool"
"go.uber.org/zap"
)
// RealTimeDataChan define channel of real time data receive
var RealTimeDataChan chan network.RealTimeDataReceiveRequest
func init() {
RealTimeDataChan = make(chan network.RealTimeDataReceiveRequest, 100)
}
// ReceiveChan define func of real time data receive and process
func ReceiveChan(ctx context.Context) {
logger := logger.GetLoggerInstance()
for {
select {
case <-ctx.Done():
return
case realTimeData := <-RealTimeDataChan:
// TODO 根据 componentID 缓存到来的实时数据
componentID := realTimeData.PayLoad.ComponentID
component, err := diagram.GetComponentMap(componentID)
if err != nil {
logger.Error("query component info from diagram map by componet id failed", zap.Int64("component_id", componentID), zap.Error(err))
continue
}
componentType := component["component_type"].(int)
if componentType != constant.DemoType {
logger.Error("can not process real time data of component type not equal DemoType", zap.Int64("component_id", componentID))
continue
}
var anchorName string
var compareValUpperLimit, compareValLowerLimit float64
var anchorRealTimeData []float64
var calculateFunc func(archorValue float64, args ...float64) float64
if anchoringV := component["anchor_v"].(bool); anchoringV {
anchorName = "voltage"
compareValUpperLimit = component["uv_alarm"].(float64)
compareValLowerLimit = component["ov_alarm"].(float64)
} else {
anchorName = "current"
compareValUpperLimit = component["ui_alarm"].(float64)
compareValLowerLimit = component["oi_alarm"].(float64)
}
componentData := map[string]interface{}{
"resistance": component["resistance"].(float64),
}
calculateFunc, params := config.SelectAnchorCalculateFuncAndParams(componentType, anchorName, componentData)
for _, param := range realTimeData.PayLoad.Values {
anchorRealTimeData = append(anchorRealTimeData, param.Value)
}
anchorConfig := config.AnchorParamConfig{
AnchorParamBaseConfig: config.AnchorParamBaseConfig{
ComponentID: componentID,
AnchorName: anchorName,
CompareValUpperLimit: compareValUpperLimit,
CompareValLowerLimit: compareValLowerLimit,
AnchorRealTimeData: anchorRealTimeData,
},
CalculateFunc: calculateFunc,
CalculateParams: params,
}
pool.GetComponentChan(ctx, componentID) <- anchorConfig
default:
}
}
}

19
sql/demo.sql Normal file
View File

@ -0,0 +1,19 @@
CREATE TABLE
public.bus_stability (
id serial NOT NULL,
component_id integer NOT NULL DEFAULT '-1'::integer,
resistance double precision NOT NULL DEFAULT 1,
anchor_v boolean NOT NULL DEFAULT false,
uv_alarm double precision NOT NULL DEFAULT 90,
ov_alarm double precision NOT NULL DEFAULT 110,
anchor_i boolean NOT NULL DEFAULT false,
ui_alarm double precision NOT NULL DEFAULT 90,
oi_alarm double precision NOT NULL DEFAULT 110,
op integer NOT NULL DEFAULT '-1'::integer,
ts timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE
public.bus_stability
ADD
CONSTRAINT bus_stability_id_primarykey PRIMARY KEY (id)

161
sql/demo/demo.sql Normal file
View File

@ -0,0 +1,161 @@
insert into
public."component" (
tag,
grid,
zone,
station,
page_id,
type,
name,
context,
op,
ts
)
values
(
'Demo1',
1,
1,
1,
1,
3,
'demo1',
'{"left":"1.2","right":"13.5","buttom":"0.2","top":"5.4"}',
1,
'2021-01-01 00:00:00'
);
insert into
public."bus_stability" (
component_id,
resistance,
anchor_v,
uv_alarm,
ov_alarm,
anchor_i,
ui_alarm,
oi_alarm,
op,
ts
)
values
(
1,
100,
false,
90,
110,
true,
90,
110,
1,
'2021-01-01 00:00:00'
);
insert into
public."component" (
tag,
grid,
zone,
station,
page_id,
type,
name,
context,
op,
ts
)
values
(
'Demo2',
1,
1,
1,
1,
3,
'demo2',
'{"left":"3.2","right":"3.5","buttom":"1.2","top":"7.4"}',
1,
'2021-01-01 00:00:00'
);
insert into
public."bus_stability" (
component_id,
resistance,
anchor_v,
uv_alarm,
ov_alarm,
anchor_i,
ui_alarm,
oi_alarm,
op,
ts
)
values
(
2,
100,
false,
90,
110,
true,
90,
110,
1,
'2021-01-01 00:00:00'
);
insert into
public."component" (
tag,
grid,
zone,
station,
page_id,
type,
name,
context,
op,
ts
)
values
(
'Demo3',
1,
1,
1,
1,
3,
'demo3',
'{"left":"1.2","right":"1.5","buttom":"4.2","top":"9.5"}',
1,
'2021-01-01 00:00:00'
);
insert into
public."bus_stability" (
component_id,
resistance,
anchor_v,
uv_alarm,
ov_alarm,
anchor_i,
ui_alarm,
oi_alarm,
op,
ts
)
values
(
3,
100,
false,
90,
110,
true,
90,
110,
1,
'2021-01-01 00:00:00'
);