implement real time data computing api
This commit is contained in:
parent
d434a7737d
commit
8cbbfbd695
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Package constants define constant variable
|
||||||
|
package constants
|
||||||
|
|
||||||
|
type contextKey string
|
||||||
|
|
||||||
|
// MeasurementUUIDKey define measurement uuid key into context
|
||||||
|
const MeasurementUUIDKey contextKey = "measurement_uuid"
|
||||||
|
|
@ -1,12 +1,18 @@
|
||||||
// Package realtimedata define real time data operation functions
|
// Package realtimedata define real time data operation functions
|
||||||
package realtimedata
|
package realtimedata
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
// ComputeConfig define struct of measurement computation
|
// ComputeConfig define struct of measurement computation
|
||||||
type ComputeConfig struct {
|
type ComputeConfig struct {
|
||||||
Cause map[string]any
|
Cause map[string]any
|
||||||
Action map[string]any
|
Action map[string]any
|
||||||
|
// TODO 预留自由调整的入口
|
||||||
|
Duration int
|
||||||
|
DataSize int64
|
||||||
|
QueryKey string
|
||||||
StopGchan chan struct{}
|
StopGchan chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ package realtimedata
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -11,6 +12,7 @@ import (
|
||||||
"modelRT/constants"
|
"modelRT/constants"
|
||||||
"modelRT/diagram"
|
"modelRT/diagram"
|
||||||
"modelRT/logger"
|
"modelRT/logger"
|
||||||
|
"modelRT/model"
|
||||||
"modelRT/network"
|
"modelRT/network"
|
||||||
"modelRT/orm"
|
"modelRT/orm"
|
||||||
"modelRT/pool"
|
"modelRT/pool"
|
||||||
|
|
@ -45,7 +47,136 @@ func StartRealTimeDataComputing(ctx context.Context, measurements []orm.Measurem
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 启动协程准备查询 redis 数据进行计算
|
conf, err := initComputeConfig(measurement)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, "failed to initialize real time compute config", "measurement_uuid", measurement.ComponentUUID, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf == nil {
|
||||||
|
logger.Info(ctx, "measurement object is disabled or does not require real time computing", "measurement_uuid", measurement.ComponentUUID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
uuidStr := measurement.ComponentUUID.String()
|
||||||
|
enrichedCtx := context.WithValue(ctx, constants.MeasurementUUIDKey, uuidStr)
|
||||||
|
conf.StopGchan = make(chan struct{})
|
||||||
|
globalComputeState.Store(uuidStr, conf)
|
||||||
|
logger.Info(ctx, "starting real time data computing for measurement", "measurement_uuid", measurement.ComponentUUID)
|
||||||
|
go continuousComputation(enrichedCtx, conf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initComputeConfig(measurement orm.Measurement) (*ComputeConfig, error) {
|
||||||
|
enableValue, exist := measurement.EventPlan["enable"]
|
||||||
|
enable, ok := enableValue.(bool)
|
||||||
|
if !exist {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("field enable can not be converted to boolean, found type: %T", enableValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !enable {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := &ComputeConfig{}
|
||||||
|
|
||||||
|
causeValue, exist := measurement.EventPlan["cause"]
|
||||||
|
if !exist {
|
||||||
|
return nil, errors.New("missing required field cause")
|
||||||
|
}
|
||||||
|
|
||||||
|
cause, ok := causeValue.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("field cause can not be converted to map[string]any, found type: %T", causeValue)
|
||||||
|
}
|
||||||
|
conf.Cause = cause
|
||||||
|
|
||||||
|
actionValue, exist := measurement.EventPlan["action"]
|
||||||
|
if !exist {
|
||||||
|
return nil, errors.New("missing required field action")
|
||||||
|
}
|
||||||
|
action, ok := actionValue.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("field action can not be converted to map[string]any, found type: %T", actionValue)
|
||||||
|
}
|
||||||
|
conf.Action = action
|
||||||
|
|
||||||
|
queryKey, err := model.GenerateMeasureIdentifier(measurement.DataSource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("generate redis query key by datasource failed: %w", err)
|
||||||
|
}
|
||||||
|
conf.QueryKey = queryKey
|
||||||
|
|
||||||
|
conf.DataSize = int64(measurement.Size)
|
||||||
|
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func continuousComputation(ctx context.Context, conf *ComputeConfig) {
|
||||||
|
client := diagram.NewRedisClient()
|
||||||
|
uuid, _ := ctx.Value(constants.MeasurementUUIDKey).(string)
|
||||||
|
// TODO duration 优化为配置项
|
||||||
|
duration := util.SecondsToDuration(1)
|
||||||
|
ticker := time.NewTicker(duration)
|
||||||
|
defer ticker.Stop()
|
||||||
|
startTimestamp := util.GenNanoTsStr()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-conf.StopGchan:
|
||||||
|
logger.Info(ctx, "continuous computing groutine stopped by local StopGchan", "uuid", uuid)
|
||||||
|
return
|
||||||
|
case <-ctx.Done():
|
||||||
|
logger.Info(ctx, "continuous computing goroutine stopped by parent context done signal")
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
stopTimestamp := util.GenNanoTsStr()
|
||||||
|
members, err := client.QueryByZRangeByLex(ctx, conf.QueryKey, conf.DataSize, startTimestamp, stopTimestamp)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, "query real time data from redis failed", "key", conf.QueryKey, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
startTimestamp = stopTimestamp
|
||||||
|
// TODO 对 redis 数据进行分析
|
||||||
|
fmt.Println(members)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processCauseMap(data map[string]any) {
|
||||||
|
keysToExtract := []string{"up", "down", "upup", "downdown"}
|
||||||
|
|
||||||
|
for _, key := range keysToExtract {
|
||||||
|
if value, exists := data[key]; exists {
|
||||||
|
// 检查类型是否为 float64
|
||||||
|
if floatVal, ok := value.(float64); ok {
|
||||||
|
fmt.Printf("键 '%s' 存在且为 float64: %.2f\n", key, floatVal)
|
||||||
|
} else {
|
||||||
|
// 键存在,但类型不对,进行错误处理或转换尝试
|
||||||
|
fmt.Printf("键 '%s' 存在但类型错误: 期望 float64, 实际 %T\n", key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeKey := "edge"
|
||||||
|
if value, exists := data[edgeKey]; exists {
|
||||||
|
if stringVal, ok := value.(string); ok {
|
||||||
|
switch stringVal {
|
||||||
|
case "raising":
|
||||||
|
fmt.Println(" -> 识别到 edge: raising")
|
||||||
|
case "falling":
|
||||||
|
fmt.Println(" -> 识别到 edge: falling")
|
||||||
|
default:
|
||||||
|
fmt.Println(" -> 识别到其他 edge 值")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("键 '%s' 存在但类型错误: 期望 string, 实际 %T\n", edgeKey, value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("键 '%s' 不存在\n", edgeKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue