After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 37 KiB |
@ -0,0 +1,18 @@ |
|||
FROM registry.ngaiot.com/base-images/golang-1.22:1.22-fs-3 |
|||
|
|||
WORKDIR /app/ |
|||
COPY . . |
|||
RUN pwd \ |
|||
&& ls \ |
|||
&& go env -w GOPROXY=https://goproxy.cn \ |
|||
&& go env -w GOPRIVATE=gitea.ngaiot.com \ |
|||
&& go env -w GO111MODULE=on |
|||
|
|||
RUN cd containerApp \ |
|||
&& CGO_ENABLED=0 go build -a -v -o ./app.exe main.go |
|||
|
|||
FROM registry.ngaiot.com/base-images/golang-1.22:1.22-fs-3 |
|||
WORKDIR /app/ |
|||
COPY --from=0 /app/*.exe /app/*.yaml /app/ |
|||
|
|||
CMD ["/app/app.exe"] |
@ -0,0 +1,7 @@ |
|||
FROM docker.io/library/alpine:3.20 |
|||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories |
|||
WORKDIR /app/ |
|||
RUN apk add --no-cache tzdata |
|||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone |
|||
COPY *.exe ./ |
|||
CMD ["/app/app.exe"] |
@ -0,0 +1,122 @@ |
|||
apiVersion: apps/v1 #指定API版本标签 |
|||
kind: StatefulSet #定义资源的类型/角色,deployment为控制器,service,endpoints |
|||
metadata: #定义资源的元数据信息 |
|||
name: etgo #定义资源的名称,在同一个namespace空间中必须是唯一的 |
|||
namespace: lk #默认default |
|||
labels: #定义资源标签 |
|||
app: yaml-etgo |
|||
spec: |
|||
replicas: 2 |
|||
selector: #定义选择器 |
|||
matchLabels: #匹配上边的标签 |
|||
app: yaml-etgo #名称 |
|||
serviceName: etgo-svc |
|||
template: #定义模板 |
|||
metadata: |
|||
labels: |
|||
app: yaml-etgo |
|||
spec: |
|||
containers: |
|||
- name: yaml-etgo #容器名,与标签名要相同 |
|||
image: registry.ngaiot.com/local/git-etgo:34 |
|||
imagePullPolicy: IfNotPresent |
|||
ports: |
|||
- containerPort: 50001 |
|||
name: tcp-50001 |
|||
- containerPort: 50000 |
|||
name: tcp-50000 |
|||
- containerPort: 40000 |
|||
name: tcp-40000 |
|||
volumeMounts: |
|||
- name: config |
|||
subPath: config.yaml |
|||
mountPath: /app/config.yaml |
|||
volumes: # volumes和container处于同一层级,别搞错了 |
|||
- name: config |
|||
configMap: |
|||
name: config-etgo |
|||
|
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: etgo-svc |
|||
namespace: lk |
|||
spec: |
|||
ports: |
|||
- name: tcp-50001 |
|||
port: 50001 |
|||
targetPort: 50001 |
|||
- name: tcp-50000 |
|||
port: 50000 |
|||
targetPort: 50000 |
|||
- name: tcp-40000 |
|||
port: 40000 |
|||
targetPort: 40000 |
|||
selector: |
|||
app: yaml-etgo |
|||
type: ClusterIP |
|||
|
|||
--- |
|||
apiVersion: v1 |
|||
kind: ConfigMap |
|||
metadata: |
|||
name: config-etgo |
|||
namespace: lk |
|||
data: |
|||
config.yaml: | |
|||
kafka: |
|||
groupId: local_et_go |
|||
brokers: |
|||
- 10.8.30.160:30992 |
|||
topics: |
|||
data_theme: native_theme |
|||
data_raw: go_RawData |
|||
data_agg: native_agg |
|||
alarm_iota: Alert |
|||
alarm_anxinyun: native_alarm |
|||
redis: |
|||
address: 10.8.30.160:30379 |
|||
es: |
|||
enable: true |
|||
addresses: |
|||
- http://10.8.30.160:30092 |
|||
user: "" |
|||
pwd: "" |
|||
index: |
|||
raw: go_native_raws |
|||
vib: go_native_vbraws |
|||
theme: go_native_themes |
|||
group: go_native_group_themes |
|||
influxDB: |
|||
enable: false |
|||
address: http://10.8.30.160:30086 |
|||
token: EJcUCii65nAWBivWjV_7ro-V1yAiarPD41iuxJgRPZBRqXst4hSPt7L8VEhUoY_hdR0kvmPHFN5lv1wlX12t-A== |
|||
organization: dongjiang |
|||
buckets: |
|||
raw: raw |
|||
vib: vib |
|||
theme: theme |
|||
#设备数据指标 |
|||
prometheus: |
|||
port: 50001 |
|||
#测点数据推送 |
|||
push: |
|||
mqtt: |
|||
enable: false |
|||
host: 10.8.30.160 |
|||
port: 30883 |
|||
clientIdPrefix: push_et_go |
|||
kafka: |
|||
enable: false |
|||
groupId: push_et_go1 |
|||
brokers: |
|||
- 10.8.30.160:30992 |
|||
#节点信息 |
|||
master: |
|||
port: 50000 |
|||
hostNameTag: "etgo-0" #k8s多状态副本 master 节点的 hostName 标记 |
|||
node: |
|||
remoteMasterHost: etgo-0.etgo-svc.lk.svc.cluster.local #用于 node Register -> master |
|||
hostIpPrefix: 10.1. #多网卡筛选ip网段 |
|||
port: 40000 |
@ -0,0 +1,26 @@ |
|||
podTemplate { |
|||
node('pod-templ-jenkins-slave-golang') { |
|||
|
|||
env.IMAGE_NAME = "${IOT_IMAGES_REGISTRY}/${LOCAL}/${JOB_NAME}" |
|||
env.IMAGE_NAME_SHORT = "${LOCAL}/${JOB_NAME}" |
|||
env.CODE_ADDR = "${GIT_ADDRESS}/DevOps/et-go.git" |
|||
|
|||
stage('Run shell') { |
|||
git branch: 'dev', credentialsId: 'gitea-builder', url: "${CODE_ADDR}" |
|||
|
|||
container('image-builder') { |
|||
sh''' |
|||
echo "当前目===" |
|||
pwd |
|||
echo "========" |
|||
ls |
|||
echo "========" |
|||
/kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=build/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup |
|||
''' |
|||
} |
|||
|
|||
buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}" |
|||
buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,40 @@ |
|||
podTemplate { |
|||
node('pod-templ-jenkins-slave-golang') { |
|||
|
|||
env.IMAGE_NAME = "${IOT_IMAGES_REGISTRY}/${LOCAL}/${JOB_NAME}" |
|||
env.IMAGE_NAME_SHORT = "${LOCAL}/${JOB_NAME}" |
|||
env.CODE_ADDR = "${GIT_ADDRESS}/DevOps/et-go.git" |
|||
|
|||
stage('Run shell') { |
|||
git branch: 'dev', credentialsId: 'gitea-builder', url: "${CODE_ADDR}" |
|||
container('golang-builder-1-22') { |
|||
sh''' |
|||
git version |
|||
git config --global --add url."https://read-only:rEADoNLY7963@gitea.ngaiot.com/".insteadOf "https://gitea.ngaiot.com/" |
|||
unset GOPROXY |
|||
go env -w GOPROXY=https://goproxy.cn,direct |
|||
go env -w GO111MODULE=on |
|||
go env -w GOPRIVATE=gitea.ngaiot.com |
|||
go env -w GOSUMDB=sum.golang.org |
|||
go env |
|||
go build -a -v -o app.exe containerApp/main.go |
|||
tar -cvf app.tar *.exe *.yaml |
|||
''' |
|||
} |
|||
|
|||
container('image-builder') { |
|||
sh''' |
|||
echo "当前目===" |
|||
pwd |
|||
ls |
|||
echo "========" |
|||
/kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=build/Dockerfile_app --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup |
|||
''' |
|||
} |
|||
archiveArtifacts artifacts: 'app.tar', followSymlinks: false |
|||
|
|||
buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}" |
|||
buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,56 @@ |
|||
kafka: |
|||
groupId: lucas_et_go3 |
|||
brokers: |
|||
- 10.8.30.142:30992 |
|||
topics: |
|||
data_theme: native_theme |
|||
data_raw: RawData |
|||
data_agg: native_agg |
|||
alarm_iota: Alert |
|||
alarm_anxinyun: native_alarm |
|||
redis: |
|||
address: 10.8.30.142:30379 |
|||
es: |
|||
enable: true |
|||
addresses: |
|||
- http://10.8.30.160:30092 |
|||
user: "" |
|||
pwd: "" |
|||
index: |
|||
raw: go_native_raws |
|||
vib: go_native_vbraws |
|||
theme: go_native_themes |
|||
group: go_native_group_themes |
|||
influxDB: |
|||
enable: true |
|||
address: http://10.8.30.160:30086 |
|||
token: EJcUCii65nAWBivWjV_7ro-V1yAiarPD41iuxJgRPZBRqXst4hSPt7L8VEhUoY_hdR0kvmPHFN5lv1wlX12t-A== |
|||
organization: dongjiang |
|||
buckets: |
|||
raw: raw |
|||
vib: vib |
|||
theme: theme |
|||
#设备数据指标 |
|||
prometheus: |
|||
port: 50001 |
|||
|
|||
#测点数据推送 |
|||
push: |
|||
mqtt: |
|||
enable: false |
|||
host: 10.8.30.142 |
|||
port: 30883 |
|||
clientIdPrefix: push_et_go |
|||
kafka: |
|||
enable: false |
|||
groupId: push_et_go_lk |
|||
brokers: |
|||
- 10.8.30.160:30992 |
|||
# 节点信息 # |
|||
master: |
|||
port: 50000 |
|||
hostNameTag: "0" #多状态副本 master 节点的 hostName 标记 |
|||
node: |
|||
remoteMasterHost: 10.8.30.110 #用于 node Register -> master |
|||
hostIpPrefix: 10.8. #多网卡筛选ip网段 |
|||
port: 40000 |
@ -0,0 +1,28 @@ |
|||
module containerApp |
|||
|
|||
go 1.22.0 |
|||
|
|||
require gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
|
|||
require ( |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,41 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"log" |
|||
etMaster "master/app" |
|||
"net/http" |
|||
_ "net/http/pprof" |
|||
etNode "node/app" |
|||
"os" |
|||
) |
|||
|
|||
func main() { |
|||
//获取状态集合序号
|
|||
hostName, err := os.Hostname() //"etgo-0"
|
|||
if err != nil { |
|||
log.Printf("Error getting hostname:%s", err.Error()) |
|||
return |
|||
} |
|||
|
|||
masterTag := configLoad.LoadConfig().GetString("master.hostNameTag") |
|||
log.Printf("hostName =[%s],masterTag=[%s]", hostName, masterTag) |
|||
if hostName == masterTag { |
|||
log.Printf("启动类型:master => hostName=[%s]", hostName) |
|||
etMaster.Start() |
|||
} else { |
|||
pprofRun() |
|||
log.Printf("启动类型:node => hostName=[%s]", hostName) |
|||
etNode.Start() |
|||
} |
|||
|
|||
} |
|||
|
|||
func pprofRun() { |
|||
|
|||
pprofAddr := ":10000" |
|||
log.Printf("性能分析 => pprofAddr=[%s]", pprofAddr) |
|||
go func() { |
|||
http.ListenAndServe(pprofAddr, nil) //开启一个http服务,nil表示绑定默认路由器DefaultServeMux
|
|||
}() |
|||
} |
@ -0,0 +1,30 @@ |
|||
package dataSource |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"sync" |
|||
) |
|||
|
|||
type DataChannels struct { |
|||
RawDataChan chan common_models.IotaData |
|||
AggDataChan chan common_models.AggData |
|||
} |
|||
|
|||
var ( |
|||
once sync.Once |
|||
dataChannels *DataChannels |
|||
) |
|||
|
|||
func InitChannels() *DataChannels { |
|||
once.Do(func() { |
|||
dataChannels = &DataChannels{ |
|||
RawDataChan: make(chan common_models.IotaData, 1), |
|||
AggDataChan: make(chan common_models.AggData, 1), |
|||
} |
|||
}) |
|||
return dataChannels |
|||
} |
|||
|
|||
func GetChannels() *DataChannels { |
|||
return dataChannels |
|||
} |
@ -0,0 +1,69 @@ |
|||
module dataSource |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
) |
|||
|
|||
require ( |
|||
github.com/IBM/sarama v1.43.0 // indirect |
|||
github.com/allegro/bigcache v1.2.1 // indirect |
|||
github.com/beorn7/perks v1.0.1 // indirect |
|||
github.com/cespare/xxhash/v2 v2.2.0 // indirect |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
|||
github.com/eapache/go-resiliency v1.6.0 // indirect |
|||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect |
|||
github.com/eapache/queue v1.1.0 // indirect |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect |
|||
github.com/eko/gocache/lib/v4 v4.1.5 // indirect |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 // indirect |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 // indirect |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/golang/mock v1.6.0 // indirect |
|||
github.com/golang/protobuf v1.5.3 // indirect |
|||
github.com/golang/snappy v0.0.4 // indirect |
|||
github.com/gorilla/websocket v1.5.0 // indirect |
|||
github.com/hashicorp/errwrap v1.0.0 // indirect |
|||
github.com/hashicorp/go-multierror v1.1.1 // indirect |
|||
github.com/hashicorp/go-uuid v1.0.3 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect |
|||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect |
|||
github.com/jcmturner/gofork v1.7.6 // indirect |
|||
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect |
|||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect |
|||
github.com/klauspost/compress v1.17.7 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/pierrec/lz4/v4 v4.1.21 // indirect |
|||
github.com/prometheus/client_golang v1.14.0 // indirect |
|||
github.com/prometheus/client_model v0.3.0 // indirect |
|||
github.com/prometheus/common v0.37.0 // indirect |
|||
github.com/prometheus/procfs v0.8.0 // indirect |
|||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect |
|||
github.com/redis/go-redis/v9 v9.5.1 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/crypto v0.19.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/net v0.21.0 // indirect |
|||
golang.org/x/sync v0.6.0 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
google.golang.org/protobuf v1.31.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,36 @@ |
|||
package kafka |
|||
|
|||
import ( |
|||
"dataSource" |
|||
"encoding/json" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
type AggDataHandler struct{} |
|||
|
|||
func (h AggDataHandler) HandleMessage(message string) bool { |
|||
// aggDataMsg: {"date":"2024-04-19T01:10:59.999+0000","sensorId":106,"structId":1,"factorId":11,"aggTypeId":2006,"aggMethodId":3004,"agg":{"strain":-19.399999618530273},"changed":{"strain":-3}}
|
|||
// aggDataMsg 中的时间为UTC格式 2024-04-19T01:10:59.999+0000,
|
|||
// 在进行 json.Unmarshal() 时报错
|
|||
// 解决方案:先将 +0000 -> +00:00,然后再将 UTC 时间转换为 +08:00 时区时间
|
|||
|
|||
// 将 "+0000" 替换为 "+00:00"
|
|||
replacedStr := strings.Replace(message, "+0000", "+00:00", 1) |
|||
|
|||
aggData := common_models.AggData{} |
|||
err := json.Unmarshal([]byte(replacedStr), &aggData) |
|||
if err != nil { |
|||
log.Printf("json parse error: %v", err) |
|||
return false |
|||
} |
|||
|
|||
// 将 UTC 时间加上8小时得到中国的本地时间
|
|||
aggData.Date = aggData.Date.Add(8 * time.Hour) |
|||
|
|||
log.Printf("handler 处理[%d]消息", aggData.SensorId) |
|||
dataSource.GetChannels().AggDataChan <- aggData |
|||
return true |
|||
} |
@ -0,0 +1,22 @@ |
|||
package kafka |
|||
|
|||
import ( |
|||
"dataSource" |
|||
"encoding/json" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
) |
|||
|
|||
type IotaDataHandler struct{} |
|||
|
|||
func (h IotaDataHandler) HandleMessage(message string) bool { |
|||
// 处理 alarm 消息
|
|||
rawData := common_models.IotaData{} |
|||
err := json.Unmarshal([]byte(message), &rawData) |
|||
if err != nil { |
|||
return false |
|||
} |
|||
log.Printf("handler 处理[%s|%s]消息", rawData.DeviceId, rawData.TriggerTime) |
|||
dataSource.GetChannels().RawDataChan <- rawData |
|||
return true |
|||
} |
@ -0,0 +1,69 @@ |
|||
package kafka |
|||
|
|||
import ( |
|||
"dataSource" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"gitea.anxinyun.cn/container/common_utils/kafkaHelper" |
|||
) |
|||
|
|||
type KafkaDataSource struct { |
|||
groupId string |
|||
brokers []string |
|||
topics map[string]string |
|||
DataChannels *dataSource.DataChannels |
|||
} |
|||
|
|||
func NewKafkaDataSource() *KafkaDataSource { |
|||
// 初始化所有通道
|
|||
dataSource.InitChannels() |
|||
|
|||
// 读取配置
|
|||
config := configLoad.LoadConfig() |
|||
groupId := config.GetString("kafka.groupId") |
|||
brokers := config.GetStringSlice("kafka.brokers") |
|||
topics := config.GetStringMapString("kafka.topics") |
|||
|
|||
return &KafkaDataSource{ |
|||
groupId: groupId, |
|||
brokers: brokers, |
|||
topics: topics, |
|||
DataChannels: dataSource.GetChannels(), |
|||
} |
|||
} |
|||
|
|||
// Producer 将 kafka message -> 各数据模型 -> 各数据通道
|
|||
func (s *KafkaDataSource) Producer() { |
|||
// 消费数据
|
|||
kafkaConsumer := kafkaHelper.NewConsumerGroupHandler(s.brokers, s.groupId) |
|||
for cfgName, topic := range s.topics { |
|||
// 创建消息处理器
|
|||
handler := NewMessageHandler(cfgName) |
|||
if handler == nil { |
|||
fmt.Printf("No handler found for topic %s\n", cfgName) |
|||
continue |
|||
} |
|||
// 订阅主题 和 消息处理
|
|||
kafkaConsumer.Subscribe(topic, handler.HandleMessage) |
|||
} |
|||
|
|||
kafkaConsumer.Worker() |
|||
} |
|||
|
|||
// IMessageHandler 是 kafka 消息处理者接口
|
|||
type IMessageHandler interface { |
|||
HandleMessage(message string) bool |
|||
} |
|||
|
|||
// NewMessageHandler 是 MessageHandler 构造函数
|
|||
// cfgName: config.yaml 中 kafka.topics 的配置名
|
|||
func NewMessageHandler(cfgName string) IMessageHandler { |
|||
switch cfgName { |
|||
case "data_raw": |
|||
return IotaDataHandler{} |
|||
case "alarm_agg": |
|||
return AggDataHandler{} |
|||
default: |
|||
return nil |
|||
} |
|||
} |
@ -0,0 +1,78 @@ |
|||
package et_Info |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"log" |
|||
"node/stages" |
|||
) |
|||
|
|||
type InfoHandler struct { |
|||
configHelper *common_utils.ConfigHelper |
|||
stage *stages.Stage |
|||
} |
|||
|
|||
func NewInfoHandler() *InfoHandler { |
|||
redisAddr := configLoad.LoadConfig().GetString("redis.address") |
|||
the := &InfoHandler{ |
|||
configHelper: common_utils.NewConfigHelper(redisAddr), |
|||
stage: stages.NewStage("测点信息获取"), |
|||
} |
|||
the.stage.AddProcess(the.getStationInfo) |
|||
return the |
|||
} |
|||
|
|||
func (the *InfoHandler) GetStage() stages.Stage { |
|||
return *the.stage |
|||
} |
|||
|
|||
func (the *InfoHandler) getStationInfo(p *common_models.ProcessData) *common_models.ProcessData { |
|||
|
|||
s, err := the.configHelper.GetDeviceStationObjs(p.DeviceData.DeviceId) |
|||
if err == nil && s != nil { |
|||
p.Stations = s |
|||
} else { |
|||
//无法查询到完整的 重新获取
|
|||
stationIds, err := the.configHelper.GetDeviceStationIds(p.DeviceData.DeviceId) |
|||
if err != nil { |
|||
|
|||
} |
|||
p.Stations, err = the.configHelper.GetStations(stationIds...) |
|||
the.getFormulaInfo(p) |
|||
err = the.configHelper.SetDeviceStationObjs(p.DeviceData.DeviceId, p.Stations) |
|||
if err != nil { |
|||
log.Printf("缓存异常 err=%s", err.Error()) |
|||
} |
|||
} |
|||
//补全 设备数据输入单位
|
|||
p.DeviceData.RawUnit = p.DeviceData.DeviceInfo.DeviceMeta.GetOutputUnit() |
|||
the.getThresholdInfo(p) |
|||
return p |
|||
} |
|||
|
|||
func (the *InfoHandler) getFormulaInfo(p *common_models.ProcessData) { |
|||
for i, _ := range p.Stations { |
|||
deviceFactorProto, err := the.configHelper.GetDeviceFactorProto(p.Stations[i].Info.ProtoCode, p.DeviceData.DeviceInfo.DeviceMeta.Id) |
|||
p.Stations[i].Info.Proto, err = the.configHelper.GetProto(p.Stations[i].Info.ProtoCode) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
for i2, device := range p.Stations[i].Info.Devices { |
|||
formulaInfo, err := the.configHelper.GetFormulaInfo(device.FormulaId) |
|||
if err == nil { |
|||
p.Stations[i].Info.Devices[i2].FormulaInfo = formulaInfo |
|||
p.Stations[i].Info.Devices[i2].DeviceFactorProto = deviceFactorProto |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
func (the *InfoHandler) getThresholdInfo(p *common_models.ProcessData) { |
|||
|
|||
for _, stationInfo := range p.Stations { |
|||
the.configHelper.GetStationThreshold(stationInfo.Info.Id) |
|||
} |
|||
} |
@ -0,0 +1,51 @@ |
|||
module et_Info |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
) |
|||
|
|||
require ( |
|||
github.com/allegro/bigcache v1.2.1 // indirect |
|||
github.com/beorn7/perks v1.0.1 // indirect |
|||
github.com/cespare/xxhash/v2 v2.2.0 // indirect |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect |
|||
github.com/eko/gocache/lib/v4 v4.1.5 // indirect |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 // indirect |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 // indirect |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/golang/mock v1.6.0 // indirect |
|||
github.com/golang/protobuf v1.5.3 // indirect |
|||
github.com/gorilla/websocket v1.5.0 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/prometheus/client_golang v1.14.0 // indirect |
|||
github.com/prometheus/client_model v0.3.0 // indirect |
|||
github.com/prometheus/common v0.37.0 // indirect |
|||
github.com/prometheus/procfs v0.8.0 // indirect |
|||
github.com/redis/go-redis/v9 v9.5.1 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/net v0.21.0 // indirect |
|||
golang.org/x/sync v0.6.0 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
google.golang.org/protobuf v1.31.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,38 @@ |
|||
package et_Info |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
"testing" |
|||
) |
|||
|
|||
var processDataStrWSD_noFormula = ` |
|||
{"DeviceData":{"DeviceId":"ed0f1d94-49a9-415b-9336-f965a2b0a985","Name":"温湿度传感器m1","ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructId":3,"TaskId":"0e1c7d3d-257d-4763-a335-198aef0fc625","AcqTime":"2024-03-20T04:20:48.000125336+08:00","RealTime":"2024-03-20T04:20:48.000251942+08:00","ErrCode":0,"Raw":{"Temp":27.9,"humidy":13.5},"RawUnit":{"Temp":"℃","humidy":"%"},"DeviceInfo":{"id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","name":"温湿度传感器m1","structure":{"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","id":3,"name":"添加边坡","type":"边坡","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"d5bf6f22-3d7a-4ab0-9043-1020e9516bcc","name":"温湿度传感器","model":"FS-BDS-WSD","properties":[],"capabilities":[{"capabilityCategoryId":3,"id":"d2add1b3-b21c-420b-82a4-e0c55ee3a019","name":"采集","properties":[{"category":"Output","name":"Temp","showName":"温度","unit":"℃"},{"category":"Output","name":"humidy","showName":"湿度","unit":"%"}]}]}},"DimensionId":"76c75371-bb9a-4f71-a25d-58adf7938296","DataType":""},"Stations":[{"Info":{"id":197,"name":"温湿度测点1","structure":3,"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","struct_name":"添加边坡","factor":2,"manual_data":false,"formula":0,"params_value":null,"Factor":{"id":2,"name":"环境温湿度","protoCode":"1002","protoName":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}],"units":{"humidity":"%RH","temperature":"℃"}},"proto":"1002","Proto":{"code":"1002","name":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}]},"Devices":[{"formula_id":0,"params":{},"iota_device_id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","iota_device_serial":0,"FormulaInfo":{"id":0,"expression":"","params":null,"ioFields":{"input":null,"output":null},"type":""},"DeviceFactorProto":{"formula":0,"field_val":{"Temp":"temperature","humidy":"humidity"},"FieldValUnitK":null}}],"Labels":"{}","CombineInfo":""},"Data":{"DeviceCalcData":null,"ThemeData":null,"CollectTime":"0001-01-01T00:00:00Z","AlarmLevel":0},"Threshold":{"Items":[{"item":3,"field_name":"temperature","name":"温度","level":1,"lower":20,"upper":100000,"begin":null,"end":null},{"item":3,"field_name":"temperature","name":"温度","level":2,"lower":10,"upper":20,"begin":null,"end":null},{"item":4,"field_name":"humidity","name":"湿度","level":1,"lower":6,"upper":16,"begin":null,"end":null}]}}]}` |
|||
var processDataStrYB_Formula303 = ` |
|||
{"DeviceData":{"DeviceId":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","Name":"GXGS16-2","ThingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","StructId":1,"TaskId":"ad6a62f1-0af7-4c53-80dd-c1d7833cdf38","AcqTime":"2024-01-16T09:40:02.921+08:00","RealTime":"0001-01-01T00:00:00Z","ErrCode":0,"Raw":{"frequency":32906,"physicalvalue":-10,"waterlevel":-10},"RawUnit":{"am":"mv","frequency":"Hz","physicalvalue":"με","temperature":"℃"},"DeviceInfo":{"id":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","name":"GXGS16-2","structure":{"thingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","id":1,"name":"东江大桥","type":"桥梁","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"13ee1f91-04cc-48af-820e-ffc0ed179b6c","name":"表面式应变计","model":"FS-BM50","properties":[{"category":"Constant","name":"sensortype","showName":"传感器类型","unit":""},{"category":"Constant","name":"protocolcode","showName":"协议号","unit":""},{"category":"Constant","name":"deviceType","showName":"设备类型","unit":""},{"category":"Constant","name":"range","showName":"量程","unit":"με"}],"capabilities":[{"capabilityCategoryId":3,"id":"5d3b0ddc-308b-4ad6-93cd-7aefd34b600f","name":"RTU","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mv"}]},{"capabilityCategoryId":3,"id":"77becc38-82f7-4612-83e2-10e0ebf5e6e3","name":"采集","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mv"}]},{"capabilityCategoryId":3,"id":"99a6c9c1-0f57-45bc-a1ff-e98a56faef46","name":"云采集","properties":[{"category":"Output","name":"am","showName":"幅值","unit":"mV"},{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"}]},{"capabilityCategoryId":3,"id":"4bd4c44c-2dca-4de3-9e9e-39b2bebc57a5","name":"微功耗","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mV"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"}]}]}},"DimensionId":"a460675c-fb42-4f9e-80b9-d50b51597618","DataType":""},"Stations":[{"Info":{"id":106,"name":"DJ-RSG-G08-001-02","structure":1,"thingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","struct_name":"东江大桥","factor":11,"manual_data":false,"formula":0,"params_value":null,"Factor":{"id":11,"name":"结构应变","protoCode":"3001","protoName":"应变","items":[{"id":39,"name":"应变","field_name":"strain","unit_name":"με","precision":0}],"units":{"strain":"με"}},"proto":"3001","Proto":{"code":"3001","name":"应变","items":[{"id":39,"name":"应变","field_name":"strain","unit_name":"με","precision":0}]},"Devices":[{"formula_id":303,"params":{"Ti":178,"To":20,"k":1.2},"iota_device_id":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","iota_device_serial":0,"FormulaInfo":{"id":303,"expression":"y=x-k*(Ti-To)","params":[{"name":"k","alias":"温补系数","unit":"","default":0},{"name":"Ti","alias":"温度T,通过关联的温度测点id来获取","unit":"","default":0},{"name":"To","alias":"初始温度","unit":"","default":0}],"ioFields":{"input":null,"output":null},"type":"sequence"},"DeviceFactorProto":{"formula":303,"field_val":{"physicalvalue":"x","y":"strain"},"FieldValUnitK":null}}],"Labels":"{}","CombineInfo":""},"Data":{"DeviceCalcData":null,"ThemeData":null,"CollectTime":"0001-01-01T00:00:00Z","AlarmLevel":0},"Threshold":{"Items":[{"item":39,"field_name":"strain","name":"应变","level":3,"lower":20,"upper":30.5,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":3,"lower":-30.5,"upper":-20,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":2,"lower":30.5,"upper":100,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":2,"lower":-100,"upper":-30.5,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":1,"lower":100,"upper":100000,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":1,"lower":-100000,"upper":-100,"begin":null,"end":null}]}}]} |
|||
` |
|||
|
|||
func TestGetInfo(t *testing.T) { |
|||
pd := &common_models.ProcessData{} |
|||
json.Unmarshal([]byte(processDataStrWSD_noFormula), pd) |
|||
infoHandler := NewInfoHandler() |
|||
sd := infoHandler.getStationInfo(pd) |
|||
bs, er := json.Marshal(sd) |
|||
if er != nil { |
|||
log.Println(string(bs)) |
|||
} |
|||
println("=====") |
|||
} |
|||
|
|||
func TestGetInfo_Formula303(t *testing.T) { |
|||
pd := &common_models.ProcessData{} |
|||
json.Unmarshal([]byte(processDataStrYB_Formula303), pd) |
|||
infoHandler := NewInfoHandler() |
|||
sd := infoHandler.getStationInfo(pd) |
|||
bs, er := json.Marshal(sd) |
|||
if er != nil { |
|||
log.Println(string(bs)) |
|||
} |
|||
println("=====") |
|||
} |
@ -0,0 +1,145 @@ |
|||
package et_analyze |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"gitea.anxinyun.cn/container/common_utils/kafkaHelper" |
|||
"log" |
|||
"strconv" |
|||
) |
|||
|
|||
type AggThresholdHandler struct { |
|||
alarmTopic string |
|||
configHelper *common_utils.ConfigHelper |
|||
kafkaAsyncProducer *kafkaHelper.KafkaAsyncProducer |
|||
} |
|||
|
|||
func NewAggThresholdHandler() *AggThresholdHandler { |
|||
configYaml := configLoad.LoadConfig() |
|||
redisAdd := configYaml.GetString("redis.address") |
|||
kafkaBrokers := configYaml.GetStringSlice("kafka.brokers") |
|||
alarmTopic := configYaml.GetString("kafka.topics.alarm_anxinyun") |
|||
|
|||
return &AggThresholdHandler{ |
|||
alarmTopic: alarmTopic, |
|||
configHelper: common_utils.NewConfigHelper(redisAdd), |
|||
kafkaAsyncProducer: kafkaHelper.NewKafkaAsyncProducer(kafkaBrokers), |
|||
} |
|||
} |
|||
|
|||
// ProcessData 进行 aggData 阈值分析,向 kafka 发送 alarm 消息
|
|||
func (t *AggThresholdHandler) ProcessData(aggData *common_models.AggData) { |
|||
if aggData == nil || aggData.Changed == nil || len(aggData.Changed) == 0 { |
|||
log.Printf("aggData 非法数据:[%v]\n", aggData) |
|||
return |
|||
} |
|||
|
|||
alarmMsg := t.judgeThreshold(aggData) |
|||
if alarmMsg != nil { |
|||
stationInfo, _ := t.configHelper.GetStationInfo(aggData.SensorId) |
|||
alarmMsg.Sponsor = common_models.Alarm_Sponsor_Recv |
|||
jsonData, err := json.Marshal(alarmMsg) |
|||
if err != nil { |
|||
fmt.Printf("测点[%d-%s][kafka-topic:%s][%v]Error marshalling JSON:%s \n", stationInfo.Id, stationInfo.Name, t.alarmTopic, alarmMsg, err) |
|||
} |
|||
|
|||
// 发布 kafka 消息
|
|||
fmt.Printf("测点[%d-%s][kafka-topic:%s]%s\n", stationInfo.Id, stationInfo.Name, t.alarmTopic, jsonData) |
|||
t.kafkaAsyncProducer.Publish(t.alarmTopic, jsonData) |
|||
} |
|||
} |
|||
|
|||
func (t *AggThresholdHandler) judgeThreshold(aggData *common_models.AggData) *common_models.AlarmMsg { |
|||
var aggTypeMap = map[int]string{ |
|||
2001: "日聚集", |
|||
2002: "周聚集", |
|||
2003: "月聚集", |
|||
2004: "年聚集", |
|||
2005: "时聚集", |
|||
2006: "10分钟聚集", |
|||
} |
|||
aggTypeStr := aggTypeMap[aggData.AggTypeId] |
|||
if aggTypeStr == "" { |
|||
aggTypeStr = "其他聚集" |
|||
} |
|||
|
|||
// 检查测点是否有聚集阈值配置信息
|
|||
aggThreshold, err := t.configHelper.GetAggThreshold(aggData.StructId, aggData.FactorId) |
|||
if err != nil || aggThreshold.Items == nil || len(aggThreshold.Items) == 0 { |
|||
log.Printf("未配置aggThreshold,无须进行阈值判断:[structId:%d factorId:%d] \n", aggData.StructId, aggData.FactorId) |
|||
return nil |
|||
} |
|||
|
|||
factor, err := t.configHelper.GetFactorInfo(aggData.FactorId) |
|||
if err != nil || factor.Items == nil || len(factor.Items) == 0 { |
|||
log.Printf("获取factor异常:[structId:%d factorId:%d]Error: %v \n", aggData.StructId, aggData.FactorId, err) |
|||
return nil |
|||
} |
|||
|
|||
var ls []common_models.ThresholdAlarmDetail |
|||
|
|||
// Changed map[string]float64 // 变化量
|
|||
for fieldName, val := range aggData.Changed { |
|||
protoItem := factor.GetProtoItem(fieldName) |
|||
if protoItem == nil { |
|||
log.Printf("测点[%d-%d-%d][%s]aggData[%v],但是Redis中没有对应的[protoItem:%s]。\n", |
|||
aggData.StructId, aggData.FactorId, aggData.SensorId, aggData.Date, aggData, fieldName) |
|||
} else { |
|||
protoItemThs := aggThreshold.GetThresholdsByItem(aggThreshold.Items, protoItem.Id) |
|||
overThresholdItem := aggThreshold.FindThresholdInRange(protoItemThs, val) |
|||
if overThresholdItem != nil { |
|||
// content 格式如:应变的10分钟聚集变化率:-0.60με,超1级阈值[-1~-0.5]
|
|||
content := fmt.Sprintf("%s的%s变化率:%.2f%s,超%d级阈值[%s]", protoItem.Name, aggTypeStr, val, protoItem.UnitName, overThresholdItem.Level, overThresholdItem.RangeText()) |
|||
ls = append(ls, common_models.ThresholdAlarmDetail{Level: overThresholdItem.Level, Content: content}) |
|||
} |
|||
//log.Printf("[aggThreshold judgeThreshold] 测点[sensorId: %d] fieldName: %s, ChangedVal (float64): %f\n", aggData.SensorId, fieldName, val)
|
|||
} |
|||
} |
|||
|
|||
alarmMsg := t.getAndCacheAlarmMsg(aggData, ls) |
|||
return alarmMsg |
|||
} |
|||
|
|||
func (t *AggThresholdHandler) getAndCacheAlarmMsg(aggData *common_models.AggData, ls []common_models.ThresholdAlarmDetail) *common_models.AlarmMsg { |
|||
stationInfo, err := t.configHelper.GetStationInfo(aggData.SensorId) |
|||
if err != nil { |
|||
log.Printf("[%v]测点不存在\n", aggData) |
|||
return nil |
|||
} |
|||
|
|||
if ls != nil && len(ls) > 0 { |
|||
// 超阈值告警
|
|||
alarm := common_models.NewOverChangingRateThreshold(findMinLevel(ls), stringifyThresholds(ls)) |
|||
if alarm == nil { |
|||
log.Printf("[%v] over-agg-threshold:[Level:%d] content:[%s]ERROR: 未找到对应告警等级的告警码。", aggData, alarm.Level, alarm.Content) |
|||
return nil |
|||
} |
|||
log.Printf("[%v] over-agg-threshold:[Level:%d] content:[%s]", aggData, alarm.Level, alarm.Content) |
|||
|
|||
// 将测点信息 -> 告警信息
|
|||
alarmMsg := alarm.ToAlarmMsg(stationInfo, aggData.Date) |
|||
|
|||
// Redis 中添加告警记录, key = alarm:2:100
|
|||
redisKey := common_models.AlarmRedisKey(common_models.ALARM_SOURCE_STATION, strconv.Itoa(aggData.SensorId)) |
|||
t.configHelper.SAddAlarm(redisKey, alarm.AlarmType) |
|||
|
|||
return &alarmMsg |
|||
|
|||
} else { |
|||
// Redis 中删除告警记录,key = alarm:2:100
|
|||
redisKey := common_models.AlarmRedisKey(common_models.ALARM_SOURCE_STATION, strconv.Itoa(aggData.SensorId)) |
|||
affects := t.configHelper.SRemAlarm(redisKey, common_models.Alarm_Type_Over_ChangeRate_Threshold) |
|||
|
|||
// 如果Redis中存在告警,则要发送恢复告警
|
|||
if affects > 0 { |
|||
alarm := common_models.NewRecoverChangingRateThreshold() |
|||
alarmMsg := alarm.ToAlarmMsg(stationInfo, aggData.Date) |
|||
return &alarmMsg |
|||
} |
|||
} |
|||
|
|||
return nil |
|||
} |
@ -0,0 +1,53 @@ |
|||
package et_analyze |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"strings" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
// 时变化速率 应变 (-,-2);(2,+); (-2,-1);(1,2);
|
|||
var aggDataJson_alarm = ` |
|||
{"date":"2024-04-19T01:10:59.999+0000","sensorId":106,"structId":1,"factorId":11,"aggTypeId":2006,"aggMethodId":3004,"agg":{"strain":-19.399999618530273},"changed":{"strain":-3}}` |
|||
var aggDataJson_normal = ` |
|||
{"date":"2024-04-19T01:20:59.999+0000","sensorId":106,"structId":1,"factorId":11,"aggTypeId":2006,"aggMethodId":3004,"agg":{"strain":-16.399999618530273},"changed":{"strain":0}}` |
|||
|
|||
func Test_aggThreshold_processData(t *testing.T) { |
|||
// aggData 中的时间为UTC格式 2024-04-19T01:10:59.999+0000,
|
|||
// 在进行 json.Unmarshal() 时报错
|
|||
// 解决方案:先将 +0000 -> +00:00,然后再将 UTC 时间转换为 +08:00 时区时间
|
|||
|
|||
// 将 "+0000" 替换为 "+00:00"
|
|||
replacedStr := strings.Replace(aggDataJson_alarm, "+0000", "+00:00", 1) |
|||
aggData := &common_models.AggData{} |
|||
err := json.Unmarshal([]byte(replacedStr), aggData) |
|||
if err != nil { |
|||
fmt.Printf("json parse error: %v", err) |
|||
return |
|||
} |
|||
|
|||
// 将 UTC 时间加上8小时得到中国的本地时间
|
|||
aggData.Date = aggData.Date.Add(8 * time.Hour) |
|||
|
|||
handler := NewAggThresholdHandler() |
|||
handler.ProcessData(aggData) |
|||
time.Sleep(2 * time.Second) // 不同协程,需要等待处理
|
|||
|
|||
// 重复发送一条,是否生成恢复告警?
|
|||
// 将 "+0000" 替换为 "+00:00"
|
|||
replacedStr = strings.Replace(aggDataJson_normal, "+0000", "+00:00", 1) |
|||
aggData_noAlarm := &common_models.AggData{} |
|||
json.Unmarshal([]byte(replacedStr), aggData_noAlarm) |
|||
// 将 UTC 时间加上8小时得到中国的本地时间
|
|||
aggData_noAlarm.Date = aggData_noAlarm.Date.Add(8 * time.Hour) |
|||
|
|||
handler.ProcessData(aggData_noAlarm) |
|||
time.Sleep(2 * time.Second) // 不同协程,需要等待处理
|
|||
|
|||
println("=====") |
|||
|
|||
// aggThreshold 直接在 recv 处理
|
|||
} |
@ -0,0 +1,83 @@ |
|||
//et_analyze 模块功能说明: |
|||
// |
|||
//1. 测量值阈值分析: |
|||
//集成方式:接入在 etNode 的处理环节中,实现 etNode.stage 的处理环节 |
|||
//接收:ProcessData 类型数据 |
|||
//输出:ProcessData 类型数据 、config.yaml 配置文件中的kafka.topics.alarm_anxinyun 主题消息 |
|||
//处理:接收 ProcessData 数据,通过对 ProcessData.Staion[n].Data.ThemeData 进行阈值分析,向kafka服务器发布【阈值告警】和【恢复告警消息】,对 ProcessData.Staion[n].Data.AlarmLevel 进行更新。 |
|||
// |
|||
//2. 变化速率阈值分析: |
|||
//集成方式:创建新的RPC服务 aggNode,无数据后处理环节 |
|||
//接收:config.yaml 配置文件中的kafka.topics.data_agg 主题消息 |
|||
//输出:config.yaml 配置文件中的kafka.topics.alarm_anxinyun 主题消息 |
|||
//处理:将接收到的kafka消息转为AggData类型数据,对AggData.Changed 进行阈值分析,向kafka服务器发布【阈值告警】和【恢复告警消息】。 |
|||
|
|||
module et_analyze |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
) |
|||
|
|||
require ( |
|||
github.com/IBM/sarama v1.43.0 // indirect |
|||
github.com/allegro/bigcache v1.2.1 // indirect |
|||
github.com/beorn7/perks v1.0.1 // indirect |
|||
github.com/cespare/xxhash/v2 v2.2.0 // indirect |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
|||
github.com/eapache/go-resiliency v1.6.0 // indirect |
|||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect |
|||
github.com/eapache/queue v1.1.0 // indirect |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect |
|||
github.com/eko/gocache/lib/v4 v4.1.5 // indirect |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 // indirect |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 // indirect |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/golang/mock v1.6.0 // indirect |
|||
github.com/golang/protobuf v1.5.3 // indirect |
|||
github.com/golang/snappy v0.0.4 // indirect |
|||
github.com/gorilla/websocket v1.5.0 // indirect |
|||
github.com/hashicorp/errwrap v1.0.0 // indirect |
|||
github.com/hashicorp/go-multierror v1.1.1 // indirect |
|||
github.com/hashicorp/go-uuid v1.0.3 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect |
|||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect |
|||
github.com/jcmturner/gofork v1.7.6 // indirect |
|||
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect |
|||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect |
|||
github.com/klauspost/compress v1.17.7 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/pierrec/lz4/v4 v4.1.21 // indirect |
|||
github.com/prometheus/client_golang v1.14.0 // indirect |
|||
github.com/prometheus/client_model v0.3.0 // indirect |
|||
github.com/prometheus/common v0.37.0 // indirect |
|||
github.com/prometheus/procfs v0.8.0 // indirect |
|||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect |
|||
github.com/redis/go-redis/v9 v9.5.1 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/crypto v0.19.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/net v0.21.0 // indirect |
|||
golang.org/x/sync v0.6.0 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
google.golang.org/protobuf v1.31.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,172 @@ |
|||
package et_analyze |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"gitea.anxinyun.cn/container/common_utils/kafkaHelper" |
|||
"log" |
|||
"node/stages" |
|||
"strconv" |
|||
) |
|||
|
|||
type ThresholdHandler struct { |
|||
alarmTopic string // 本模块业务字段
|
|||
stage *stages.Stage // 必须
|
|||
configHelper *common_utils.ConfigHelper // 可选
|
|||
kafkaAsyncProducer *kafkaHelper.KafkaAsyncProducer // 可选
|
|||
kafkaAlarmTopic string // 可选
|
|||
} |
|||
|
|||
func NewThresholdHandler() *ThresholdHandler { |
|||
configYaml := configLoad.LoadConfig() |
|||
redisAdd := configYaml.GetString("redis.address") |
|||
kafkaBrokers := configYaml.GetStringSlice("kafka.brokers") |
|||
alarmTopic := configYaml.GetString("kafka.topics.alarm_anxinyun") |
|||
|
|||
model := &ThresholdHandler{ |
|||
alarmTopic: alarmTopic, |
|||
stage: stages.NewStage("阈值判断"), |
|||
configHelper: common_utils.NewConfigHelper(redisAdd), |
|||
kafkaAsyncProducer: kafkaHelper.NewKafkaAsyncProducer(kafkaBrokers), |
|||
kafkaAlarmTopic: alarmTopic, |
|||
} |
|||
|
|||
model.stage.AddProcess(model.processData) |
|||
return model |
|||
} |
|||
|
|||
func (the *ThresholdHandler) GetStage() stages.Stage { |
|||
return *the.stage |
|||
} |
|||
|
|||
// 必须
|
|||
func (t *ThresholdHandler) processData(resultData *common_models.ProcessData) *common_models.ProcessData { |
|||
if resultData == nil || resultData.Stations == nil || len(resultData.Stations) == 0 { |
|||
return resultData |
|||
} |
|||
|
|||
for i := range resultData.Stations { |
|||
station := &resultData.Stations[i] |
|||
alarmMsg := t.judgeThreshold(station) |
|||
|
|||
//fmt.Printf("测点[%d-%s]kafka[topic:%s][content:%s]\n", station.Info.Id, station.Info.Name, t.alarmTopic, alarmMsg.Content)
|
|||
if alarmMsg != nil { |
|||
// 设置发起者
|
|||
alarmMsg.Sponsor = common_models.Alarm_Sponsor_Threshold |
|||
jsonData, err := json.Marshal(alarmMsg) |
|||
if err != nil { |
|||
log.Printf("测点[%d-%s][kafka-topic:%s][%v]Error marshalling JSON:%s \n", station.Info.Id, station.Info.Name, t.alarmTopic, alarmMsg, err) |
|||
continue |
|||
} |
|||
|
|||
// 发布 kafka 消息
|
|||
log.Printf("测点[%d-%s][kafka-topic:%s]%s\n", station.Info.Id, station.Info.Name, t.alarmTopic, jsonData) |
|||
t.kafkaAsyncProducer.Publish(t.alarmTopic, jsonData) |
|||
} |
|||
} |
|||
|
|||
return resultData |
|||
} |
|||
|
|||
func (t *ThresholdHandler) judgeThreshold(station *common_models.Station) *common_models.AlarmMsg { |
|||
// 检查测点是否有阈值配置信息
|
|||
if station.Threshold == nil || station.Threshold.Items == nil { |
|||
log.Printf("测点[%d-%s]未配置阈值,无须进行阈值判断\n", station.Info.Id, station.Info.Name) |
|||
return nil |
|||
} |
|||
|
|||
// 获取测点数据的阈值配置
|
|||
filteredItems := station.Threshold.GetThresholdsByTime(station.Data.CollectTime) |
|||
if len(filteredItems) == 0 { |
|||
log.Printf("测点[%d-%s][%s]无对应阈值项,无须进行阈值判断\n", station.Info.Id, station.Info.Name, station.Data.CollectTime) |
|||
return nil |
|||
} |
|||
|
|||
var ls []common_models.ThresholdAlarmDetail |
|||
for fieldName, themeVal := range station.Data.ThemeData { |
|||
if val, ok := themeVal.(float64); ok { |
|||
protoItem := station.Info.Proto.GetProtoItem(fieldName) |
|||
if protoItem == nil { |
|||
log.Printf("测点[%d-%s][%s][%s]themeData[%v],但是Redis中没有对应的[protoItem:%s]。\n", |
|||
station.Info.Id, station.Info.Name, station.Data.CollectTime, fieldName, station.Data.ThemeData, fieldName) |
|||
} else { |
|||
protoItemThs := station.Threshold.GetThresholdsByItem(filteredItems, protoItem.Id) |
|||
overThresholdItem := station.Threshold.FindThresholdInRange(protoItemThs, val) |
|||
if overThresholdItem != nil { |
|||
// content 格式如:1:湿度采集值:13.50%RH,超1级阈值[6~16]
|
|||
content := fmt.Sprintf("%s采集值:%.2f%s,超%d级阈值[%s]", protoItem.Name, val, protoItem.UnitName, overThresholdItem.Level, overThresholdItem.RangeText()) |
|||
ls = append(ls, common_models.ThresholdAlarmDetail{Level: overThresholdItem.Level, Content: content}) |
|||
} |
|||
//fmt.Printf("[threshold judgeThreshold] 测点[%d-%s] fieldName: %s, ThemeVal (float64): %f\n", station.Info.Id, station.Info.Name, fieldName, val)
|
|||
} |
|||
} else { |
|||
log.Printf("[threshold judgeThreshold] 测点[%d-%s] fieldName: %s, ThemeVal cannot be converted to float64\n", station.Info.Id, station.Info.Name, fieldName) |
|||
} |
|||
} |
|||
|
|||
// []ThresholdAlarmDetail -> AlarmMsg
|
|||
alarmMsg := t.getAndCacheAlarmMsg(station, ls) |
|||
return alarmMsg |
|||
} |
|||
|
|||
// GetAndCacheAlarmMsg 获取和缓存 AlarmMsg
|
|||
func (t *ThresholdHandler) getAndCacheAlarmMsg(station *common_models.Station, ls []common_models.ThresholdAlarmDetail) *common_models.AlarmMsg { |
|||
if ls != nil && len(ls) > 0 { |
|||
// 超阈值告警
|
|||
alarm := common_models.NewAlarmOverThreshold(findMinLevel(ls), stringifyThresholds(ls)) |
|||
if alarm == nil { |
|||
log.Printf("over-threshold [%d-%s] level:%d code:%s content:%s time:%s ERROR: 未找到对应告警等级的告警码。\n", |
|||
station.Info.Id, station.Info.Name, alarm.Level, alarm.Code, alarm.Content, station.Data.CollectTime) |
|||
return nil |
|||
} |
|||
|
|||
// 给测点数据设置告警等级
|
|||
station.Data.AlarmLevel = alarm.Level |
|||
log.Printf("over-threshold [%d-%s] level:%d code:%s content:%s time:%s\n", |
|||
station.Info.Id, station.Info.Name, alarm.Level, alarm.Code, alarm.Content, station.Data.CollectTime) |
|||
|
|||
// 将测点信息 -> 告警信息
|
|||
alarmMsg := alarm.ToAlarmMsg(station.Info, station.Data.CollectTime) |
|||
|
|||
// Redis 中添加告警记录, key = alarm:2:100
|
|||
redisKey := common_models.AlarmRedisKey(common_models.ALARM_SOURCE_STATION, strconv.Itoa(station.Info.Id)) |
|||
t.configHelper.SAddAlarm(redisKey, alarm.AlarmType) |
|||
|
|||
return &alarmMsg |
|||
|
|||
} else { |
|||
// Redis 中删除告警记录, key = alarm:2:100
|
|||
redisKey := common_models.AlarmRedisKey(common_models.ALARM_SOURCE_STATION, strconv.Itoa(station.Info.Id)) |
|||
affects := t.configHelper.SRemAlarm(redisKey, common_models.Alarm_Type_Over_Threshold) |
|||
if affects > 0 { |
|||
// 如果Redis中存在告警,则要发送恢复告警
|
|||
alarm := common_models.NewAlarmRecoverThreshold() |
|||
alarmMsg := alarm.ToAlarmMsg(station.Info, station.Data.CollectTime) |
|||
return &alarmMsg |
|||
} |
|||
} |
|||
|
|||
// 既没有产生新告警,也没有需要恢复的告警
|
|||
return nil |
|||
} |
|||
|
|||
func findMinLevel(ls []common_models.ThresholdAlarmDetail) int { |
|||
minLevel := ls[0].Level |
|||
for _, detail := range ls { |
|||
if detail.Level < minLevel { |
|||
minLevel = detail.Level |
|||
} |
|||
} |
|||
return minLevel |
|||
} |
|||
|
|||
func stringifyThresholds(ls []common_models.ThresholdAlarmDetail) string { |
|||
str := "" |
|||
for _, detail := range ls { |
|||
str += fmt.Sprintf("%d:%s;", detail.Level, detail.Content) |
|||
} |
|||
return str |
|||
} |
@ -0,0 +1,44 @@ |
|||
package et_analyze |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
var processDataStrWSD_noFormula = ` |
|||
{"DeviceData":{"DeviceId":"ed0f1d94-49a9-415b-9336-f965a2b0a985","Name":"温湿度传感器m1","ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructId":3,"TaskId":"0e1c7d3d-257d-4763-a335-198aef0fc625","AcqTime":"2024-03-20T04:20:48.000125336+08:00","RealTime":"2024-03-20T04:20:48.000251942+08:00","ErrCode":0,"Raw":{"Temp":27.9,"humidy":13.5},"RawUnit":{"Temp":"℃","humidy":"%"},"DeviceInfo":{"id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","name":"温湿度传感器m1","structure":{"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","id":3,"name":"添加边坡","type":"边坡","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"d5bf6f22-3d7a-4ab0-9043-1020e9516bcc","name":"温湿度传感器","model":"FS-BDS-WSD","properties":[],"capabilities":[{"capabilityCategoryId":3,"id":"d2add1b3-b21c-420b-82a4-e0c55ee3a019","name":"采集","properties":[{"category":"Output","name":"Temp","showName":"温度","unit":"℃"},{"category":"Output","name":"humidy","showName":"湿度","unit":"%"}]}]}},"DimensionId":"76c75371-bb9a-4f71-a25d-58adf7938296","DataType":""},"Stations":[{"Info":{"id":197,"name":"温湿度测点1","structure":3,"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","struct_name":"添加边坡","factor":2,"manual_data":false,"formula":0,"params_value":null,"Factor":{"id":2,"name":"环境温湿度","protoCode":"1002","protoName":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}],"units":{"humidity":"%RH","temperature":"℃"}},"proto":"1002","Proto":{"code":"1002","name":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}]},"Devices":[{"formula_id":0,"params":{},"iota_device_id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","iota_device_serial":0,"FormulaInfo":{"id":0,"expression":"","params":null,"ioFields":{"input":null,"output":null},"type":""},"DeviceFactorProto":{"formula":0,"field_val":{"Temp":"temperature","humidy":"humidity"},"FieldValUnitK":null}}],"Labels":"{}","CombineInfo":""},"Data":{"DeviceCalcData":null,"ThemeData":{"humidity":13.5,"temperature":27.9},"CollectTime":"2024-03-20T04:20:48.000125336+08:00","AlarmLevel":0},"Threshold":{"Items":[{"item":3,"field_name":"temperature","name":"温度","level":1,"lower":20,"upper":100000,"begin":null,"end":null},{"item":3,"field_name":"temperature","name":"温度","level":2,"lower":10,"upper":20,"begin":null,"end":null},{"item":4,"field_name":"humidity","name":"湿度","level":1,"lower":6,"upper":16,"begin":null,"end":null}]}}]}` |
|||
var processDataStrWSD_noAlarm = ` |
|||
{"DeviceData":{"DeviceId":"ed0f1d94-49a9-415b-9336-f965a2b0a985","Name":"温湿度传感器m1","ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructId":3,"TaskId":"0e1c7d3d-257d-4763-a335-198aef0fc625","AcqTime":"2024-03-20T04:20:48.000125336+08:00","RealTime":"2024-03-20T04:20:48.000251942+08:00","ErrCode":0,"Raw":{"Temp":4,"humidy":4},"RawUnit":{"Temp":"℃","humidy":"%"},"DeviceInfo":{"id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","name":"温湿度传感器m1","structure":{"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","id":3,"name":"添加边坡","type":"边坡","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"d5bf6f22-3d7a-4ab0-9043-1020e9516bcc","name":"温湿度传感器","model":"FS-BDS-WSD","properties":[],"capabilities":[{"capabilityCategoryId":3,"id":"d2add1b3-b21c-420b-82a4-e0c55ee3a019","name":"采集","properties":[{"category":"Output","name":"Temp","showName":"温度","unit":"℃"},{"category":"Output","name":"humidy","showName":"湿度","unit":"%"}]}]}},"DimensionId":"76c75371-bb9a-4f71-a25d-58adf7938296","DataType":""},"Stations":[{"Info":{"id":197,"name":"温湿度测点1","structure":3,"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","struct_name":"添加边坡","factor":2,"manual_data":false,"formula":0,"params_value":null,"Factor":{"id":2,"name":"环境温湿度","protoCode":"1002","protoName":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}],"units":{"humidity":"%RH","temperature":"℃"}},"proto":"1002","Proto":{"code":"1002","name":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}]},"Devices":[{"formula_id":0,"params":{},"iota_device_id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","iota_device_serial":0,"FormulaInfo":{"id":0,"expression":"","params":null,"ioFields":{"input":null,"output":null},"type":""},"DeviceFactorProto":{"formula":0,"field_val":{"Temp":"temperature","humidy":"humidity"},"FieldValUnitK":null}}],"Labels":"{}","CombineInfo":""},"Data":{"DeviceCalcData":null,"ThemeData":{"humidity":4,"temperature":4},"CollectTime":"2024-03-20T04:20:48.000125336+08:00","AlarmLevel":0},"Threshold":{"Items":[{"item":3,"field_name":"temperature","name":"温度","level":1,"lower":20,"upper":100000,"begin":null,"end":null},{"item":3,"field_name":"temperature","name":"温度","level":2,"lower":10,"upper":20,"begin":null,"end":null},{"item":4,"field_name":"humidity","name":"湿度","level":1,"lower":6,"upper":16,"begin":null,"end":null}]}}]}` |
|||
|
|||
func Test_processData(t *testing.T) { |
|||
pd := &common_models.ProcessData{} |
|||
json.Unmarshal([]byte(processDataStrWSD_noFormula), pd) |
|||
handler := NewThresholdHandler() |
|||
sd := handler.processData(pd) |
|||
time.Sleep(2 * time.Second) // 不同协程,需要等待处理
|
|||
|
|||
// 打印处理后的数据
|
|||
bs, er := json.Marshal(sd) |
|||
if er != nil { |
|||
log.Println(string(bs)) |
|||
} |
|||
|
|||
// 重复发送一条,是否生成恢复告警?
|
|||
pd_noAlarm := &common_models.ProcessData{} |
|||
json.Unmarshal([]byte(processDataStrWSD_noAlarm), pd_noAlarm) |
|||
sd_noAlarm := handler.processData(pd_noAlarm) |
|||
time.Sleep(2 * time.Second) // 不同协程,需要等待处理
|
|||
bs, er = json.Marshal(sd_noAlarm) |
|||
if er != nil { |
|||
log.Println(string(bs)) |
|||
} |
|||
|
|||
println("=====") |
|||
|
|||
// 经过 et_analyze 处理后的数据,会设置数据的告警等级信息:station.Data.AlarmLevel = alarm.Level
|
|||
// var analyzed_StrWSD_noFormula = `{"DeviceData":{"DeviceId":"ed0f1d94-49a9-415b-9336-f965a2b0a985","Name":"温湿度传感器m1","ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructId":3,"TaskId":"0e1c7d3d-257d-4763-a335-198aef0fc625","AcqTime":"2024-03-20T04:20:48.000125336+08:00","RealTime":"2024-03-20T04:20:48.000251942+08:00","ErrCode":0,"Raw":{"Temp":27.9,"humidy":13.5},"RawUnit":{"Temp":"℃","humidy":"%"},"DeviceInfo":{"id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","name":"温湿度传感器m1","structure":{"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","id":3,"name":"添加边坡","type":"边坡","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"d5bf6f22-3d7a-4ab0-9043-1020e9516bcc","name":"温湿度传感器","model":"FS-BDS-WSD","properties":[],"capabilities":[{"capabilityCategoryId":3,"id":"d2add1b3-b21c-420b-82a4-e0c55ee3a019","name":"采集","properties":[{"category":"Output","name":"Temp","showName":"温度","unit":"℃"},{"category":"Output","name":"humidy","showName":"湿度","unit":"%"}]}]}},"DimensionId":"76c75371-bb9a-4f71-a25d-58adf7938296","DataType":""},"Stations":[{"Info":{"id":197,"name":"温湿度测点1","structure":3,"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","struct_name":"添加边坡","factor":2,"manual_data":false,"formula":0,"params_value":null,"Factor":{"id":2,"name":"环境温湿度","protoCode":"1002","protoName":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}],"units":{"humidity":"%RH","temperature":"℃"}},"proto":"1002","Proto":{"code":"1002","name":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}]},"Devices":[{"formula_id":0,"params":{},"iota_device_id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","iota_device_serial":0,"FormulaInfo":{"id":0,"expression":"","params":null,"ioFields":{"input":null,"output":null},"type":""},"DeviceFactorProto":{"formula":0,"field_val":{"Temp":"temperature","humidy":"humidity"},"FieldValUnitK":null}}],"Labels":"{}","CombineInfo":""},"Data":{"DeviceCalcData":null,"ThemeData":{"humidity":13.5,"temperature":27.9},"CollectTime":"2024-03-20T04:20:48.000125336+08:00","AlarmLevel":1},"Threshold":{"Items":[{"item":3,"field_name":"temperature","name":"温度","level":1,"lower":20,"upper":100000,"begin":null,"end":null},{"item":3,"field_name":"temperature","name":"温度","level":2,"lower":10,"upper":20,"begin":null,"end":null},{"item":4,"field_name":"humidity","name":"湿度","level":1,"lower":6,"upper":16,"begin":null,"end":null}]}}]}`
|
|||
// 生成的 ProcessData 传递到下个环节
|
|||
} |
@ -0,0 +1,89 @@ |
|||
package et_cache |
|||
|
|||
import ( |
|||
"et_cache/cacheSer" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_models/constant/redisKey" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"log" |
|||
"node/stages" |
|||
) |
|||
|
|||
type CacheHandler struct { |
|||
cacheServer *cacheSer.CacheServer |
|||
stage *stages.Stage |
|||
} |
|||
|
|||
func NewCacheHandler() *CacheHandler { |
|||
redisAddr := configLoad.LoadConfig().GetString("redis.address") |
|||
configHelper := common_utils.NewConfigHelper(redisAddr) |
|||
the := &CacheHandler{ |
|||
stage: stages.NewStage("测点数据缓存"), |
|||
cacheServer: cacheSer.NewCacheServer(configHelper), |
|||
} |
|||
the.stage.AddProcess(the.enqueue) |
|||
return the |
|||
} |
|||
func (the *CacheHandler) GetStage() stages.Stage { |
|||
return *the.stage |
|||
} |
|||
func (the *CacheHandler) enqueue(p *common_models.ProcessData) *common_models.ProcessData { |
|||
|
|||
for _, station := range p.Stations { |
|||
for _, item := range station.Info.Proto.Items { |
|||
cacheItemKey := fmt.Sprintf("%s:%d:%s", redisKey.CacheWindow, station.Info.Id, item.FieldName) |
|||
cacheWindow, ok := the.cacheServer.ReadCacheMap(station.Info.Id, item.FieldName) |
|||
if !ok { |
|||
cacheWindow = the.cacheServer.CreatFilterWindow(station.Info.Id, item.FieldName) |
|||
} |
|||
needItemCache := common_models.AnalyzeData{} |
|||
if value, ok := station.Data.ThemeData[item.FieldName]; ok { |
|||
//目前只支持float64类型的缓存
|
|||
if v, ok := value.(float64); ok { |
|||
needItemCache = common_models.AnalyzeData{ |
|||
Raw: v, |
|||
IsValid: true, |
|||
Data: 0, |
|||
} |
|||
//滑窗计算
|
|||
isWinCalcValid := false |
|||
needItemCache.Data, isWinCalcValid = the.windowCalc(v, cacheWindow) |
|||
if isWinCalcValid { |
|||
station.Data.ThemeData[item.FieldName] = needItemCache.Data |
|||
} |
|||
} |
|||
} |
|||
//缓存
|
|||
cacheWindow.EnQueue(needItemCache) |
|||
the.cacheServer.UpdateCacheMap(cacheItemKey, cacheWindow) |
|||
} |
|||
} |
|||
return p |
|||
} |
|||
|
|||
func (the *CacheHandler) windowCalc(raw float64, window common_models.CacheWindow) (float64, bool) { |
|||
if window.Size() == 0 { |
|||
return raw, false |
|||
} |
|||
result := 0.0 |
|||
switch window.MethodId { |
|||
case common_models.Filter_CalcMedian: |
|||
result = filterForMedian(raw, window) |
|||
case common_models.Filter_LimitAmp: |
|||
result = filterForLimitAmp(raw, window) |
|||
case common_models.Filter_CalcMeanValue: |
|||
result = filterForMean(raw, window) |
|||
case common_models.Filter_CalcStvMean: |
|||
result = filterForMeanStandardDeviation(raw, window) |
|||
case common_models.Filter_CalcWindow: |
|||
//result = filterForWave(raw, window)
|
|||
case common_models.Filter_ExtreAverage: |
|||
result = filterForExtreAverage(raw, window) |
|||
default: |
|||
log.Printf("不支持滑窗公式id:[%d]", window.MethodId) |
|||
return raw, false |
|||
} |
|||
return result, true |
|||
} |
@ -0,0 +1,90 @@ |
|||
package cacheSer |
|||
|
|||
import ( |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_models/constant/redisKey" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"log" |
|||
"sync" |
|||
"time" |
|||
) |
|||
|
|||
var once sync.Once |
|||
var cacheWindowMaps sync.Map |
|||
|
|||
type CacheServer struct { |
|||
CacheWindowMaps *sync.Map |
|||
configHelper *common_utils.ConfigHelper |
|||
} |
|||
|
|||
func NewCacheServer(configHelper *common_utils.ConfigHelper) *CacheServer { |
|||
once.Do(func() { |
|||
cacheWindowMaps = sync.Map{} |
|||
}) |
|||
return &CacheServer{ |
|||
CacheWindowMaps: &cacheWindowMaps, |
|||
configHelper: configHelper, |
|||
} |
|||
} |
|||
|
|||
func (c *CacheServer) CreatFilterWindow(stationId int, itemName string) common_models.CacheWindow { |
|||
//新测点滑窗 size默认1
|
|||
k := fmt.Sprintf("%d-%s", stationId, itemName) |
|||
cacheWindow := common_models.NewCacheWindow(k, 1, 0, common_models.FilterParams{}) |
|||
Filter, err := c.configHelper.GetFilter(stationId) |
|||
if err == nil { |
|||
for _, item := range Filter.Items { |
|||
if itemName == item.FieldName { |
|||
cacheWindow = common_models.NewCacheWindow(k, item.WindowSize, item.MethodId, item.Params) |
|||
} |
|||
} |
|||
} |
|||
return cacheWindow |
|||
} |
|||
|
|||
func (c *CacheServer) UpdateCacheMap(key string, value common_models.CacheWindow) { |
|||
c.CacheWindowMaps.Store(key, value) |
|||
err := c.configHelper.SetChainedCacheObjWithExpiration(key, value.ToSaveCache(), time.Hour*6) |
|||
if err != nil { |
|||
log.Printf("updateCacheMap 异常,err=%s", err.Error()) |
|||
} |
|||
} |
|||
|
|||
// ReadCacheMap
|
|||
// read map 时 判断时间 超过2分钟 重新读取
|
|||
func (c *CacheServer) ReadCacheMap(stationId int, itemName string) (common_models.CacheWindow, bool) { |
|||
key := fmt.Sprintf("%s:%d:%s", redisKey.CacheWindow, stationId, itemName) |
|||
if obj, ok := c.CacheWindowMaps.Load(key); ok { |
|||
win, ok2 := obj.(common_models.CacheWindow) |
|||
return win, ok2 |
|||
} |
|||
if preWindow, err := c.configHelper.GetCacheWindowObj(key); err == nil { |
|||
//过期
|
|||
if preWindow.CheckExpiration() { |
|||
preData := preWindow.DeQueueAll() |
|||
reloadWindow := c.CreatFilterWindow(stationId, itemName) |
|||
if len(preData) > 0 { |
|||
for _, datum := range preData { |
|||
reloadWindow.EnQueue(datum) |
|||
} |
|||
return reloadWindow, true |
|||
} |
|||
} |
|||
return preWindow, true |
|||
} |
|||
v := c.CreatFilterWindow(stationId, itemName) |
|||
return v, true |
|||
} |
|||
|
|||
// 获取缓存总数 测试用
|
|||
func getSizeByCacheWindowMaps() int { |
|||
size := 0 |
|||
cacheWindowMaps.Range(func(key, value any) bool { |
|||
size += 1 |
|||
log.Printf("================ CacheWindowMap-key:%v", key) |
|||
return true |
|||
}) |
|||
log.Printf("================= CacheWindowMap-size:%v", size) |
|||
return size |
|||
} |
@ -0,0 +1,106 @@ |
|||
package et_cache |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_calc" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"math" |
|||
"strconv" |
|||
) |
|||
|
|||
// 中值
|
|||
func filterForMedian(curr float64, window common_models.CacheWindow) float64 { |
|||
result := curr |
|||
if dataArray, ok := window.DeQueueAllRaw(); ok { |
|||
result = common_calc.GetMedian(dataArray) |
|||
} |
|||
return result |
|||
} |
|||
|
|||
// 限幅滤波
|
|||
func filterForLimitAmp(curr float64, window common_models.CacheWindow) float64 { |
|||
result := curr |
|||
if kt, err := strconv.ParseFloat(window.Params.Kt, 64); err == nil { |
|||
if last, ok := window.Latest().(common_models.AnalyzeData); ok { |
|||
if math.Abs(curr-last.Data) > kt { |
|||
result = last.Data |
|||
} |
|||
} |
|||
} |
|||
return result |
|||
} |
|||
|
|||
// 滑动平均 (raw参与计算)
|
|||
func filterForMean(curr float64, window common_models.CacheWindow) float64 { |
|||
result := curr |
|||
if dataArray, ok := window.DeQueueAllRaw(); ok { |
|||
result = common_calc.GetAvg(dataArray) |
|||
} |
|||
return result |
|||
} |
|||
|
|||
// 方差平均
|
|||
// 这个方法具体原理不懂,复制scala 版本
|
|||
func filterForMeanStandardDeviation(curr float64, window common_models.CacheWindow) float64 { |
|||
result := curr |
|||
if ru, err := strconv.ParseFloat(window.Params.Ru, 64); err == nil { |
|||
if dataArray, ok := window.DeQueueAllRaw(); ok { |
|||
avg, standardDeviation := common_calc.MeanStandardDeviation(dataArray) |
|||
nsgm := ru * standardDeviation |
|||
more := curr - avg |
|||
if more > nsgm { |
|||
result = standardDeviation |
|||
} |
|||
} |
|||
|
|||
} |
|||
return result |
|||
} |
|||
|
|||
func filterForWave(curr float64, window common_models.CacheWindow) float64 { |
|||
result := curr |
|||
rd, err := strconv.ParseFloat(window.Params.Kt, 64) |
|||
if err != nil { |
|||
return curr |
|||
} |
|||
|
|||
rt, err := strconv.ParseFloat(window.Params.Rt, 64) |
|||
if err != nil { |
|||
return curr |
|||
} |
|||
|
|||
dt, err := strconv.ParseFloat(window.Params.Dt, 64) |
|||
if err != nil { |
|||
return curr |
|||
} |
|||
|
|||
//todo 算R
|
|||
println(rd, rt, dt) |
|||
|
|||
return result |
|||
} |
|||
|
|||
// 去极值移动平均 (data参与计算)
|
|||
func filterForExtreAverage(curr float64, window common_models.CacheWindow) float64 { |
|||
result := curr |
|||
rd, err := strconv.ParseFloat(window.Params.Kt, 64) |
|||
if err != nil { |
|||
return curr |
|||
} |
|||
|
|||
ru, err := strconv.ParseFloat(window.Params.Ru, 64) |
|||
if err != nil { |
|||
return curr |
|||
} |
|||
|
|||
//合理范围
|
|||
if curr > rd && curr < ru { |
|||
return curr |
|||
} |
|||
|
|||
//不合理
|
|||
if dataArray, ok := window.DeQueueAllData(); ok { |
|||
result = common_calc.GetAvg(dataArray) |
|||
} |
|||
|
|||
return result |
|||
} |
@ -0,0 +1,53 @@ |
|||
module et_cache |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_calc v0.0.1 |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
) |
|||
|
|||
require ( |
|||
github.com/allegro/bigcache v1.2.1 // indirect |
|||
github.com/beorn7/perks v1.0.1 // indirect |
|||
github.com/cespare/xxhash/v2 v2.2.0 // indirect |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect |
|||
github.com/eko/gocache/lib/v4 v4.1.5 // indirect |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 // indirect |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 // indirect |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/golang/mock v1.6.0 // indirect |
|||
github.com/golang/protobuf v1.5.3 // indirect |
|||
github.com/google/uuid v1.6.0 // indirect |
|||
github.com/gorilla/websocket v1.5.0 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/prometheus/client_golang v1.14.0 // indirect |
|||
github.com/prometheus/client_model v0.3.0 // indirect |
|||
github.com/prometheus/common v0.37.0 // indirect |
|||
github.com/prometheus/procfs v0.8.0 // indirect |
|||
github.com/redis/go-redis/v9 v9.5.1 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/net v0.21.0 // indirect |
|||
golang.org/x/sync v0.6.0 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
google.golang.org/protobuf v1.31.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,62 @@ |
|||
package algorithm |
|||
|
|||
import ( |
|||
"log" |
|||
"math" |
|||
"math/cmplx" |
|||
"scientificgo.org/fft" |
|||
) |
|||
|
|||
func IsPow2(N int) bool { |
|||
if N == 0 { |
|||
return false |
|||
} |
|||
return (uint64(N) & uint64(N-1)) == 0 |
|||
} |
|||
|
|||
// 频谱计算(全程分析)
|
|||
func FFTforward(raw []float64, fs float64) (float64, []float64) { |
|||
rawLen := len(raw) |
|||
power := math.Ceil(math.Log(float64(rawLen)) / math.Log(2)) |
|||
|
|||
signLen := int(math.Pow(2, power)) |
|||
|
|||
signArray := make([]float64, signLen) |
|||
copy(signArray[:], raw[:]) |
|||
|
|||
if !IsPow2(signLen) { |
|||
log.Panicf("fft源数据长度[%d]异常,不是2^n", signLen) |
|||
} |
|||
cpx := Float64ToComplex128Array(signArray) |
|||
fftData := fft.Fft(cpx, false) |
|||
fftLen := signLen / 2 |
|||
df := fs / float64(signLen) |
|||
|
|||
resp := make([]float64, fftLen) |
|||
for i := 0; i < fftLen; i++ { |
|||
resp[i] = cmplx.Abs(fftData[i]) * 2 / float64(signLen) |
|||
} |
|||
return df, resp |
|||
} |
|||
|
|||
func dfftCalc(raw []float64) []float64 { |
|||
cpx := Float64ToComplex128Array(raw) |
|||
rp := fft.Fft(cpx, false) |
|||
frp := Complex128ToFloat64Array(rp) |
|||
return frp |
|||
} |
|||
|
|||
func Float64ToComplex128Array(x []float64) []complex128 { |
|||
y := make([]complex128, len(x)) |
|||
for i, v := range x { |
|||
y[i] = complex(v, 0) |
|||
} |
|||
return y |
|||
} |
|||
func Complex128ToFloat64Array(x []complex128) []float64 { |
|||
y := make([]float64, len(x)) |
|||
for i, v := range x { |
|||
y[i] = real(v) |
|||
} |
|||
return y |
|||
} |
@ -0,0 +1,42 @@ |
|||
package algorithm |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
) |
|||
|
|||
// StrainCompensationByExternalTemperature
|
|||
// y=x-k*(T-To)
|
|||
func StrainCompensationByExternalTemperature(device common_models.SecureStationDevice, RawData map[string]any, RawsUnit map[string]string, temperature float64) (result map[string]any, unit map[string]string) { |
|||
log.Printf("开始计算 应变外联温补") |
|||
var k float64 |
|||
var x float64 |
|||
var Tid int |
|||
var To float64 |
|||
paramsMap := device.Params |
|||
if kObj, ok := paramsMap["k"]; ok { |
|||
k = kObj.(float64) |
|||
} |
|||
|
|||
if xObj, ok := RawData["physicalvalue"]; ok { |
|||
x = xObj.(float64) |
|||
} |
|||
|
|||
if TidObj, ok := paramsMap["Ti"]; ok { |
|||
Tid64 := TidObj.(float64) |
|||
Tid = int(Tid64) |
|||
} |
|||
|
|||
if ToObj, ok := paramsMap["To"]; ok { |
|||
To = ToObj.(float64) |
|||
} |
|||
T := temperature |
|||
y := x - k*(T-To) |
|||
log.Printf("%v=%v-%v*(%v-%v),Tid=%d", y, x, k, To, temperature, Tid) |
|||
result = make(map[string]any) |
|||
unit = make(map[string]string) |
|||
//注意 y是中间变量 也是公式的最终输出
|
|||
result["y"] = y |
|||
unit["y"] = "με" |
|||
return result, unit |
|||
} |
@ -0,0 +1,24 @@ |
|||
package algorithm |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_calc" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
) |
|||
|
|||
func VibCalc(vibData common_models.VibrationData) (result map[string]any, unit map[string]string) { |
|||
df, fd := FFTforward(vibData.Data, vibData.SampleFreq) |
|||
_min, _max := common_calc.MinMax(vibData.Data) |
|||
ppv := _max - _min |
|||
pv := common_calc.AbsMax(vibData.Data) |
|||
trms := common_calc.MeanSqrt(vibData.Data) |
|||
frms := common_calc.MeanSqrt(fd) / 2.0 |
|||
result = map[string]any{ |
|||
"df": df, |
|||
"pv": pv, |
|||
"ppv": ppv, |
|||
"trms": trms, |
|||
"frms": frms, |
|||
} |
|||
unit = map[string]string{} |
|||
return result, unit |
|||
} |
@ -0,0 +1,245 @@ |
|||
package et_calc |
|||
|
|||
import ( |
|||
"errors" |
|||
"et_cache/cacheSer" |
|||
"et_calc/algorithm" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_calc" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_models/constant/redisKey" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"gitea.anxinyun.cn/container/common_utils/dbHelper" |
|||
"gitea.anxinyun.cn/container/common_utils/transform" |
|||
"log" |
|||
"maps" |
|||
"node/stages" |
|||
"sort" |
|||
"strings" |
|||
) |
|||
|
|||
type CalcHandler struct { |
|||
cacheServer *cacheSer.CacheServer |
|||
unitHelper *common_utils.UnitHelper |
|||
esESHelper *dbHelper.ESHelper |
|||
stage *stages.Stage |
|||
} |
|||
|
|||
func NewCalcHandler() *CalcHandler { |
|||
redisAddr := configLoad.LoadConfig().GetString("redis.address") |
|||
esAddresses := configLoad.LoadConfig().GetStringSlice("es.addresses") |
|||
configHp := common_utils.NewConfigHelper(redisAddr) |
|||
the := &CalcHandler{ |
|||
cacheServer: cacheSer.NewCacheServer(configHp), |
|||
unitHelper: common_utils.NewUnitHelper(), |
|||
esESHelper: dbHelper.NewESHelper(esAddresses, "", ""), |
|||
stage: stages.NewStage("单测点计算"), |
|||
} |
|||
the.stage.AddProcess(the.calcFormula) |
|||
return the |
|||
} |
|||
func (the *CalcHandler) GetStage() stages.Stage { |
|||
return *the.stage |
|||
} |
|||
|
|||
// 单设备测点
|
|||
func (the *CalcHandler) calcFormula(p *common_models.ProcessData) *common_models.ProcessData { |
|||
for i := range p.Stations { |
|||
for _, device := range p.Stations[i].Info.Devices { |
|||
//计算结果
|
|||
resultData := map[string]any{} |
|||
//结果单位
|
|||
resultUnit := map[string]string{} |
|||
switch p.DeviceData.DataType { |
|||
case common_models.RawTypeVib: |
|||
//振动(一般5002)
|
|||
vibData := p.DeviceData.GetVibrationData() |
|||
// todo 物理量转换(灵敏度系数) scala => raw = raw.map(a => a / k)
|
|||
resultData, resultUnit = algorithm.VibCalc(vibData) |
|||
case common_models.RawTypeDiag: |
|||
//todo 诊断流程
|
|||
default: |
|||
//其他普通数据
|
|||
//数据类型转换 可以处理的都转换 string转float64
|
|||
RawByNum := transform.Numerical(p.DeviceData.Raw) |
|||
switch device.FormulaId { |
|||
case common_models.FormulaType_None: |
|||
resultData = RawByNum |
|||
resultUnit = p.DeviceData.RawUnit |
|||
case common_models.FormulaType_seepage: |
|||
case common_models.FormulaType_Outflow: |
|||
case common_models.FormulaType_TriSeepageEmp: |
|||
case common_models.FormulaType_AirCorrect: |
|||
case common_models.FormulaType_Interpolation: |
|||
case common_models.FormulaType_InterpolationRadar: |
|||
case common_models.FormulaType_InterpolationRadar2: |
|||
case common_models.FormulaType_DefaultMaxMin: |
|||
case common_models.FormulaType_strainCompensationByTemperature: |
|||
var err error |
|||
resultData, resultUnit, err = the.strainCompensationByTemperature(device, RawByNum, p.DeviceData.RawUnit) |
|||
if len(resultData) == 0 { |
|||
log.Printf("[%s]测点[%d]%s Fid=[%d] 计算异常", |
|||
p.Stations[i].Info.StructureName, |
|||
p.Stations[i].Info.Id, p.Stations[i].Info.Name, device.FormulaInfo.Id) |
|||
} |
|||
if err != nil { |
|||
log.Printf("[%s]测点[%d]%s Fid=[%d] 计算异常=>%s", |
|||
p.Stations[i].Info.StructureName, |
|||
p.Stations[i].Info.Id, p.Stations[i].Info.Name, device.FormulaInfo.Id, err.Error()) |
|||
} |
|||
default: |
|||
log.Printf("通用计算公式[%d]%s", device.FormulaInfo.Id, device.FormulaInfo.Expression) |
|||
resultData, resultUnit = the.formulaTemplateCalc(device, |
|||
RawByNum, |
|||
p.DeviceData.RawUnit, |
|||
) |
|||
} |
|||
} |
|||
|
|||
//测点计算后的数据->主题数据单位转换
|
|||
protoObj := p.Stations[i].Info.Proto |
|||
outMap := map[string]any{} |
|||
for inK, inV := range resultData { |
|||
inUnit := resultUnit[inK] |
|||
outKey := device.DeviceFactorProto.FieldVal[inK] |
|||
outUnit := protoObj.GetProtoItem(outKey).UnitName |
|||
k := the.unitHelper.GetUnitTransK(inUnit, outUnit) |
|||
if v, ok := inV.(float64); ok { |
|||
if outKey != "" { |
|||
outMap[outKey] = common_calc.Decimal(v*k, 5) |
|||
} else { //无映射关系的 用原有key
|
|||
outMap[inK] = common_calc.Decimal(v, 5) |
|||
} |
|||
} else { |
|||
outMap[outKey] = inV |
|||
} |
|||
|
|||
} |
|||
//todo 测点多设备特殊处理..
|
|||
if len(p.Stations[i].Info.Devices) == 1 { |
|||
p.Stations[i].Data.ThemeData = outMap |
|||
p.Stations[i].Data.CollectTime = p.DeviceData.AcqTime |
|||
} |
|||
} |
|||
} |
|||
|
|||
return p |
|||
} |
|||
func (the *CalcHandler) formulaTemplateCalc(device common_models.SecureStationDevice, RawData map[string]any, RawsUnit map[string]string) (map[string]any, map[string]string) { |
|||
paramsMap := device.Params |
|||
formulaExpression := device.FormulaInfo.Expression |
|||
//device-proto:$proto:$deviceMetaId
|
|||
// 输入字段映射(映射单位系数转换) -》 设备输入和公式输入的转换
|
|||
inMap := make(map[string]any) |
|||
outMap := make(map[string]any) |
|||
outUnitMap := make(map[string]string) |
|||
for rawK, newK := range device.DeviceFactorProto.FieldVal { |
|||
if dv, ok := RawData[rawK]; ok { |
|||
RawUnit := RawsUnit[rawK] |
|||
inUnit := device.FormulaInfo.IoFields.GetInFieldUnit(newK) |
|||
k := the.unitHelper.GetUnitTransK(RawUnit, inUnit) //需要进行 设备->公式入参 系数转换
|
|||
inMap[newK] = dv.(float64) * k |
|||
} |
|||
} |
|||
//浅拷贝
|
|||
maps.Copy(paramsMap, inMap) |
|||
|
|||
Expressions := strings.Split(formulaExpression, ";") |
|||
|
|||
for _, expression := range Expressions { |
|||
//F=ε*1e-6*E*A+F0,ε=AVG(εi)
|
|||
//子公式计算结果作为参数
|
|||
subExpressions := strings.Split(expression, ",") |
|||
for i := len(subExpressions) - 1; i > 0; i-- { |
|||
subExpression := subExpressions[i] |
|||
subResultMap := the.calcExpressionResult(subExpression, paramsMap) |
|||
maps.Copy(paramsMap, subResultMap) |
|||
} |
|||
//公式计算
|
|||
ResultMap := the.calcExpressionResult(expression, paramsMap) |
|||
for outFK, outFv := range ResultMap { |
|||
outMap[outFK] = outFv |
|||
outUnitMap[outFK] = device.FormulaInfo.IoFields.GetOutFieldUnit(outFK) |
|||
} |
|||
|
|||
} |
|||
|
|||
return outMap, outUnitMap |
|||
} |
|||
|
|||
func (the *CalcHandler) calcExpressionResult(formulaExpression string, paramMap map[string]any) map[string]any { |
|||
|
|||
//获取所有参数key
|
|||
var paramsK []string |
|||
for k := range paramMap { |
|||
paramsK = append(paramsK, k) |
|||
} |
|||
//替换的k ,优先长key 避免=> 如 pid 被 pi影响
|
|||
sort.Slice(paramsK, func(i, j int) bool { |
|||
return len([]rune(paramsK[i])) > len([]rune(paramsK[j])) |
|||
}) |
|||
|
|||
for _, pk := range paramsK { |
|||
pv := paramMap[pk] |
|||
formulaExpression = strings.Replace(formulaExpression, pk, fmt.Sprintf("%v", pv), -1) |
|||
} |
|||
fsp := strings.Split(formulaExpression, "=") |
|||
templateStr := fsp[1] |
|||
resultMap := map[string]any{fsp[0]: common_calc.CalculateFormula(templateStr)} |
|||
return resultMap |
|||
} |
|||
|
|||
// 应变温补计算数据获取
|
|||
func (the *CalcHandler) strainCompensationByTemperature(device common_models.SecureStationDevice, RawData map[string]any, RawsUnit map[string]string) (resultData map[string]any, resultUnit map[string]string, err error) { |
|||
itemName := "temperature" |
|||
itemParamKey := "Ti" |
|||
tempStationId := 0 |
|||
if TidObj, ok := device.Params[itemParamKey]; ok { |
|||
Tid64 := TidObj.(float64) |
|||
tempStationId = int(Tid64) |
|||
} else { |
|||
errMsg := fmt.Sprintf("[%s]计算公式[%d] 参数 [%s] 空", device.IotaDeviceId, device.FormulaInfo.Id, itemParamKey) |
|||
err = errors.New(errMsg) |
|||
return |
|||
} |
|||
tempWindow, valid := the.cacheServer.ReadCacheMap(tempStationId, itemName) |
|||
if tempWindow.Len() == 0 { |
|||
//redis 无 再去es中查询
|
|||
indexName := configLoad.LoadConfig().GetString("es.index.theme") |
|||
if tempEsData, err := the.esESHelper.SearchLatestStationData(indexName, tempStationId); err == nil { |
|||
if temperatureObj, ok := tempEsData.Data["temperature"]; ok { |
|||
temperature := temperatureObj.(float64) |
|||
tempWindow.EnQueueAnalyzeData(common_models.AnalyzeData{ |
|||
Raw: temperature, |
|||
IsValid: true, |
|||
Data: temperature, |
|||
}) |
|||
|
|||
cacheItemKey := fmt.Sprintf("%s:%d:%s", redisKey.CacheWindow, tempStationId, itemName) |
|||
the.cacheServer.UpdateCacheMap(cacheItemKey, tempWindow) |
|||
} |
|||
|
|||
} |
|||
} |
|||
if !valid { |
|||
errMsg := fmt.Sprintf("[%s]计算公式[%d],无关联测点[%d]温度缓存数据", device.IotaDeviceId, device.FormulaInfo.Id, tempStationId) |
|||
err = errors.New(errMsg) |
|||
return |
|||
} |
|||
|
|||
if tempObj, ok := tempWindow.LatestByAnalyzeData(); ok { |
|||
tempData := tempObj.Data |
|||
//log.Printf("tempData=%v", tempData)
|
|||
resultData, resultUnit = algorithm.StrainCompensationByExternalTemperature(device, |
|||
RawData, |
|||
RawsUnit, |
|||
tempData, |
|||
) |
|||
} else { |
|||
errMsg := fmt.Sprintf("[%s]计算公式[%d],无关联测点[%d] 有效温度数据,无法计算温补", |
|||
device.IotaDeviceId, device.FormulaInfo.Id, tempStationId) |
|||
err = errors.New(errMsg) |
|||
} |
|||
return |
|||
} |
@ -0,0 +1,41 @@ |
|||
package et_calc |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
"testing" |
|||
) |
|||
|
|||
//var processDataStrWSD_noFormula = `{"DeviceData":{"DeviceId":"ed0f1d94-49a9-415b-9336-f965a2b0a985","Name":"温湿度传感器m1","ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructId":3,"TaskId":"0e1c7d3d-257d-4763-a335-198aef0fc625","AcqTime":"2024-03-20T04:20:48.000125336+08:00","RealTime":"2024-03-20T04:20:48.000251942+08:00","ErrCode":0,"Raw":{"Temp":27.9,"humidy":13.5},"DeviceInfo":{"id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","name":"温湿度传感器m1","structure":{"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","id":3,"name":"添加边坡","type":"边坡","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"d5bf6f22-3d7a-4ab0-9043-1020e9516bcc","name":"温湿度传感器","model":"FS-BDS-WSD","properties":[],"capabilities":[{"capabilityCategoryId":3,"id":"d2add1b3-b21c-420b-82a4-e0c55ee3a019","name":"采集","properties":[{"category":"Output","name":"Temp","showName":"温度","unit":"℃"},{"category":"Output","name":"humidy","showName":"湿度","unit":"%"}]}]}},"DimensionId":"76c75371-bb9a-4f71-a25d-58adf7938296"},"StationInfo":[{"Id":197,"Name":"温湿度测点1","Structure":3,"ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructName":"","FactorId":2,"ManualData":false,"Formula":0,"ParamsValue":null,"FactorName":"","ProtoCode":"1002","Devices":[{"formula_id":0,"params":{},"iota_device_id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","iota_device_serial":0}],"Labels":"{}","CombineInfo":"","ThemeData":{"RawData":null,"ThemeData":null}},{"Id":200,"Name":"温湿度传感器m1-2","Structure":3,"ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructName":"","FactorId":2,"ManualData":false,"Formula":0,"ParamsValue":null,"FactorName":"","ProtoCode":"1002","Devices":[{"formula_id":0,"params":{},"iota_device_id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","iota_device_serial":0}],"Labels":"{}","CombineInfo":"","ThemeData":{"RawData":null,"ThemeData":null}}]}`
|
|||
//var processDataStrYB_Formula303 = `{"DeviceData":{"DeviceId":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","Name":"GXGS16-2","ThingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","StructId":1,"TaskId":"ad6a62f1-0af7-4c53-80dd-c1d7833cdf38","AcqTime":"2024-01-16T09:40:02.921+08:00","RealTime":"0001-01-01T00:00:00Z","ErrCode":0,"Raw":{"frequency":32906,"physicalvalue":-10,"waterlevel":-10},"DeviceInfo":{"id":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","name":"GXGS16-2","structure":{"thingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","id":1,"name":"东江大桥","type":"桥梁","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"13ee1f91-04cc-48af-820e-ffc0ed179b6c","name":"表面式应变计","model":"FS-BM50","properties":[{"category":"Constant","name":"sensortype","showName":"传感器类型","unit":""},{"category":"Constant","name":"protocolcode","showName":"协议号","unit":""},{"category":"Constant","name":"deviceType","showName":"设备类型","unit":""},{"category":"Constant","name":"range","showName":"量程","unit":"με"}],"capabilities":[{"capabilityCategoryId":3,"id":"5d3b0ddc-308b-4ad6-93cd-7aefd34b600f","name":"RTU","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mv"}]},{"capabilityCategoryId":3,"id":"77becc38-82f7-4612-83e2-10e0ebf5e6e3","name":"采集","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mv"}]},{"capabilityCategoryId":3,"id":"99a6c9c1-0f57-45bc-a1ff-e98a56faef46","name":"云采集","properties":[{"category":"Output","name":"am","showName":"幅值","unit":"mV"},{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"}]},{"capabilityCategoryId":3,"id":"4bd4c44c-2dca-4de3-9e9e-39b2bebc57a5","name":"微功耗","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mV"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"}]}]}},"DimensionId":"a460675c-fb42-4f9e-80b9-d50b51597618"},"StationInfo":[{"Id":106,"Name":"DJ-RSG-G08-001-02","Structure":1,"ThingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","StructName":"","FactorId":11,"ManualData":false,"Formula":0,"ParamsValue":null,"FactorName":"","ProtoCode":"3001","Devices":[{"formula_id":303,"params":{"Ti":178,"To":20,"k":1.2},"iota_device_id":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","iota_device_serial":0}],"Labels":"{}","CombineInfo":"","ThemeData":{"RawData":null,"ThemeData":null}}]}`
|
|||
|
|||
var processDataStrWSD_noFormula = ` |
|||
{"DeviceData":{"DeviceId":"ed0f1d94-49a9-415b-9336-f965a2b0a985","Name":"温湿度传感器m1","ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructId":3,"TaskId":"0e1c7d3d-257d-4763-a335-198aef0fc625","AcqTime":"2024-03-20T04:20:48.000125336+08:00","RealTime":"2024-03-20T04:20:48.000251942+08:00","ErrCode":0,"Raw":{"Temp":27.9,"humidy":13.5},"RawUnit":{"Temp":"℃","humidy":"%"},"DeviceInfo":{"id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","name":"温湿度传感器m1","structure":{"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","id":3,"name":"添加边坡","type":"边坡","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"d5bf6f22-3d7a-4ab0-9043-1020e9516bcc","name":"温湿度传感器","model":"FS-BDS-WSD","properties":[],"capabilities":[{"capabilityCategoryId":3,"id":"d2add1b3-b21c-420b-82a4-e0c55ee3a019","name":"采集","properties":[{"category":"Output","name":"Temp","showName":"温度","unit":"℃"},{"category":"Output","name":"humidy","showName":"湿度","unit":"%"}]}]}},"DimensionId":"76c75371-bb9a-4f71-a25d-58adf7938296","DataType":""},"Stations":[{"Info":{"id":197,"name":"温湿度测点1","structure":3,"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","struct_name":"添加边坡","factor":2,"manual_data":false,"formula":0,"params_value":null,"Factor":{"id":2,"name":"环境温湿度","protoCode":"1002","protoName":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}],"units":{"humidity":"%RH","temperature":"℃"}},"proto":"1002","Proto":{"code":"1002","name":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}]},"Devices":[{"formula_id":0,"params":{},"iota_device_id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","iota_device_serial":0,"FormulaInfo":{"id":0,"expression":"","params":null,"ioFields":{"input":null,"output":null},"type":""},"DeviceFactorProto":{"formula":0,"field_val":{"Temp":"temperature","humidy":"humidity"},"FieldValUnitK":null}}],"Labels":"{}","CombineInfo":""},"Data":{"DeviceCalcData":null,"ThemeData":null,"CollectTime":"0001-01-01T00:00:00Z","AlarmLevel":0},"Threshold":{"Items":[{"item":3,"field_name":"temperature","name":"温度","level":1,"lower":20,"upper":100000,"begin":null,"end":null},{"item":3,"field_name":"temperature","name":"温度","level":2,"lower":10,"upper":20,"begin":null,"end":null},{"item":4,"field_name":"humidity","name":"湿度","level":1,"lower":6,"upper":16,"begin":null,"end":null}]}}]}` |
|||
var processDataStrYB_Formula303 = ` |
|||
{"DeviceData":{"DeviceId":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","Name":"GXGS16-2","ThingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","StructId":1,"TaskId":"ad6a62f1-0af7-4c53-80dd-c1d7833cdf38","AcqTime":"2024-01-16T09:40:02.921+08:00","RealTime":"0001-01-01T00:00:00Z","ErrCode":0,"Raw":{"frequency":32906,"physicalvalue":-10,"waterlevel":-10},"RawUnit":{"am":"mv","frequency":"Hz","physicalvalue":"με","temperature":"℃"},"DeviceInfo":{"id":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","name":"GXGS16-2","structure":{"thingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","id":1,"name":"东江大桥","type":"桥梁","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"13ee1f91-04cc-48af-820e-ffc0ed179b6c","name":"表面式应变计","model":"FS-BM50","properties":[{"category":"Constant","name":"sensortype","showName":"传感器类型","unit":""},{"category":"Constant","name":"protocolcode","showName":"协议号","unit":""},{"category":"Constant","name":"deviceType","showName":"设备类型","unit":""},{"category":"Constant","name":"range","showName":"量程","unit":"με"}],"capabilities":[{"capabilityCategoryId":3,"id":"5d3b0ddc-308b-4ad6-93cd-7aefd34b600f","name":"RTU","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mv"}]},{"capabilityCategoryId":3,"id":"77becc38-82f7-4612-83e2-10e0ebf5e6e3","name":"采集","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mv"}]},{"capabilityCategoryId":3,"id":"99a6c9c1-0f57-45bc-a1ff-e98a56faef46","name":"云采集","properties":[{"category":"Output","name":"am","showName":"幅值","unit":"mV"},{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"}]},{"capabilityCategoryId":3,"id":"4bd4c44c-2dca-4de3-9e9e-39b2bebc57a5","name":"微功耗","properties":[{"category":"Output","name":"frequency","showName":"频率","unit":"Hz"},{"category":"Output","name":"physicalvalue","showName":"微应变","unit":"με"},{"category":"Output","name":"am","showName":"幅值","unit":"mV"},{"category":"Output","name":"temperature","showName":"温度","unit":"℃"}]}]}},"DimensionId":"a460675c-fb42-4f9e-80b9-d50b51597618","DataType":""},"Stations":[{"Info":{"id":106,"name":"DJ-RSG-G08-001-02","structure":1,"thingId":"8e3eec71-c924-47fd-ac8b-2f28c49ad4e9","struct_name":"东江大桥","factor":11,"manual_data":false,"formula":0,"params_value":null,"Factor":{"id":11,"name":"结构应变","protoCode":"3001","protoName":"应变","items":[{"id":39,"name":"应变","field_name":"strain","unit_name":"με","precision":0}],"units":{"strain":"με"}},"proto":"3001","Proto":{"code":"3001","name":"应变","items":[{"id":39,"name":"应变","field_name":"strain","unit_name":"με","precision":0}]},"Devices":[{"formula_id":303,"params":{"Ti":178,"To":20,"k":1.2},"iota_device_id":"c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b","iota_device_serial":0,"FormulaInfo":{"id":303,"expression":"y=x-k*(Ti-To)","params":[{"name":"k","alias":"温补系数","unit":"","default":0},{"name":"Ti","alias":"温度T,通过关联的温度测点id来获取","unit":"","default":0},{"name":"To","alias":"初始温度","unit":"","default":0}],"ioFields":{"input":null,"output":null},"type":"sequence"},"DeviceFactorProto":{"formula":303,"field_val":{"physicalvalue":"x","y":"strain"},"FieldValUnitK":null}}],"Labels":"{}","CombineInfo":""},"Data":{"DeviceCalcData":null,"ThemeData":null,"CollectTime":"0001-01-01T00:00:00Z","AlarmLevel":0},"Threshold":{"Items":[{"item":39,"field_name":"strain","name":"应变","level":3,"lower":20,"upper":30.5,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":3,"lower":-30.5,"upper":-20,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":2,"lower":30.5,"upper":100,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":2,"lower":-100,"upper":-30.5,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":1,"lower":100,"upper":100000,"begin":null,"end":null},{"item":39,"field_name":"strain","name":"应变","level":1,"lower":-100000,"upper":-100,"begin":null,"end":null}]}}]} |
|||
` |
|||
|
|||
func TestCalcBystationSingle(t *testing.T) { |
|||
pd := &common_models.ProcessData{} |
|||
json.Unmarshal([]byte(processDataStrYB_Formula303), pd) |
|||
commDataHandler := NewCalcHandler() |
|||
sd := commDataHandler.calcFormula(pd) |
|||
bs, er := json.Marshal(sd) |
|||
if er != nil { |
|||
log.Println(string(bs)) |
|||
} |
|||
println("=====") |
|||
} |
|||
|
|||
func TestCalcBystationSingle_noFormula(t *testing.T) { |
|||
pd := &common_models.ProcessData{} |
|||
json.Unmarshal([]byte(processDataStrWSD_noFormula), pd) |
|||
commDataHandler := NewCalcHandler() |
|||
sd := commDataHandler.calcFormula(pd) |
|||
bs, er := json.Marshal(sd) |
|||
if er != nil { |
|||
log.Println(string(bs)) |
|||
} |
|||
println("=====") |
|||
} |
@ -0,0 +1,57 @@ |
|||
module et_calc |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_calc v0.0.1 |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
github.com/stretchr/testify v1.9.0 |
|||
scientificgo.org/fft v0.0.0 |
|||
) |
|||
|
|||
require ( |
|||
github.com/allegro/bigcache v1.2.1 // indirect |
|||
github.com/beorn7/perks v1.0.1 // indirect |
|||
github.com/cespare/xxhash/v2 v2.2.0 // indirect |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect |
|||
github.com/eko/gocache/lib/v4 v4.1.5 // indirect |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 // indirect |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 // indirect |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/golang/mock v1.6.0 // indirect |
|||
github.com/golang/protobuf v1.5.3 // indirect |
|||
github.com/google/uuid v1.6.0 // indirect |
|||
github.com/gorilla/websocket v1.5.0 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect |
|||
github.com/prometheus/client_golang v1.14.0 // indirect |
|||
github.com/prometheus/client_model v0.3.0 // indirect |
|||
github.com/prometheus/common v0.37.0 // indirect |
|||
github.com/prometheus/procfs v0.8.0 // indirect |
|||
github.com/redis/go-redis/v9 v9.5.1 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/net v0.21.0 // indirect |
|||
golang.org/x/sync v0.6.0 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
google.golang.org/protobuf v1.31.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,233 @@ |
|||
package group |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_models/constant/settlementParam" |
|||
"log" |
|||
"time" |
|||
) |
|||
|
|||
// 沉降分组业务处理说明:
|
|||
// dimensionId 对应WEB端配置:组网配置/采集策略
|
|||
// taskId是维度下的schema每次调用的时候,生成一个唯一的
|
|||
// 分组配置要求:分组计算中的测点的【采集策略】同一个周期采集
|
|||
// 特殊场景:上报类测点,要进行分组计算,需要在协议里处理,输出_acq_number确保一致
|
|||
|
|||
type CalcTask struct { |
|||
*BaseDueTask |
|||
stationGroup *common_models.StationGroup |
|||
dimensionId string // 第一个测点归属的采集维度
|
|||
taskId string |
|||
acqTime time.Time |
|||
stationMap map[int]common_models.Station |
|||
} |
|||
|
|||
func NewGroupCalcTask(group *common_models.StationGroup, dimensionId string, taskId string, acqTime time.Time) *CalcTask { |
|||
return &CalcTask{ |
|||
BaseDueTask: NewBaseDueTask(), |
|||
stationGroup: group, |
|||
dimensionId: dimensionId, |
|||
taskId: taskId, |
|||
acqTime: acqTime, |
|||
stationMap: make(map[int]common_models.Station), |
|||
} |
|||
} |
|||
|
|||
func (t *CalcTask) AddStationData(data common_models.Station) { |
|||
if data.Data.ThemeData == nil || len(data.Data.ThemeData) == 0 { |
|||
return |
|||
} |
|||
|
|||
t.stationMap[data.Info.Id] = data |
|||
} |
|||
|
|||
func (t *CalcTask) GetStationData(stationId int) *common_models.Station { |
|||
if station, ok := t.stationMap[stationId]; ok { |
|||
return &station |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// CheckIntegrity 检查计算项是否完整
|
|||
func (t *CalcTask) CheckIntegrity() bool { |
|||
for _, item := range t.stationGroup.AllCorrItems() { |
|||
if _, ok := t.stationMap[item.StationId]; !ok { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
// SetTimeout 根据第一个测点归属的采集维度类型,设置任务超时时长
|
|||
func (t *CalcTask) SetTimeout() int { |
|||
var expireSeconds int |
|||
|
|||
if t.stationMap == nil || len(t.stationMap) == 0 { |
|||
expireSeconds = DefaultExpire() |
|||
} |
|||
|
|||
if t.dimensionId == "" { |
|||
expireSeconds = DefaultExpire() |
|||
} else { |
|||
expireSeconds = FromDimension(t.dimensionId) |
|||
} |
|||
|
|||
t.SetDeadLineTime(expireSeconds) |
|||
return expireSeconds |
|||
} |
|||
|
|||
// ToDump 输出分组计算信息
|
|||
func (t *CalcTask) ToDump() string { |
|||
corrItemsBytes, err := json.Marshal(t.stationGroup.AllCorrItems()) |
|||
if err != nil { |
|||
fmt.Println("[et_calc.group.CalcTask.ToDump()] Error marshalling JSON:", err) |
|||
} |
|||
return fmt.Sprintf("【groupId:%d taskId:%s acqTime:%s】 %d/%v", t.stationGroup.Id, t.taskId, t.acqTime, len(t.stationMap), string(corrItemsBytes)) |
|||
} |
|||
|
|||
// Calc 沉降分组计算 TODO 计算完成后返回分组的数据(要重新定义结构体)
|
|||
func (t *CalcTask) Calc() []common_models.Station { |
|||
// 基点信息异常时数据处理:将 themeData 转储到 phyData, 再将 themeData 设置为 map[string]any{};
|
|||
funcSetErrorData := func() []common_models.Station { |
|||
var result []common_models.Station |
|||
for _, objStation := range t.stationMap { |
|||
objStation.Data.PyhData = objStation.Data.ThemeData |
|||
objStation.Data.ThemeData = map[string]any{} |
|||
result = append(result, objStation) |
|||
} |
|||
return result |
|||
} |
|||
|
|||
var resultStations []common_models.Station |
|||
baseItem := t.stationGroup.GetSettlementBaseItem() |
|||
if baseItem == nil || t.stationMap[baseItem.StationId].Info.Id == 0 { |
|||
// 无基点的分组数据处理
|
|||
return funcSetErrorData() |
|||
} else { // 有基点,进行减基点计算
|
|||
baseStation := t.stationMap[baseItem.StationId] |
|||
_, ok := baseStation.Data.GetValidThemeData() |
|||
if !ok { |
|||
log.Printf("基点数据计算失败,无效的主题数据。%s\n", baseStation.LogMsg()) |
|||
return funcSetErrorData() |
|||
} |
|||
|
|||
// baseValidFields 有效的监测项,主题数据的 field 必须在监测原型中存在
|
|||
base_themeData_fields := baseStation.Data.GetThemeFields() |
|||
base_proto_fields := baseStation.GetProtoFields() |
|||
baseValidFields, ok := t.filterFields(base_themeData_fields, base_proto_fields) |
|||
if !ok { |
|||
log.Printf("基点数据计算失败,无效的监测项(数据 field 必须在监测原型中存在)。%s\n", baseStation.LogMsg()) |
|||
return funcSetErrorData() |
|||
} |
|||
|
|||
// 基点数据(包含虚拟基点的计算)
|
|||
virtualBase := t.calcVirtualSettlementBase(*baseItem, baseValidFields) |
|||
if virtualBase == nil || len(virtualBase) == 0 { |
|||
log.Printf("虚拟基点数据计算失败。%s\n", baseStation.LogMsg()) |
|||
return funcSetErrorData() |
|||
} |
|||
|
|||
// 减基点计算
|
|||
for _, groupItem := range t.stationGroup.Items { |
|||
objStation, ok := t.stationMap[groupItem.StationId] |
|||
if !ok { |
|||
continue |
|||
} |
|||
|
|||
if groupItem.ParamsValue[settlementParam.Base] == true { // 基点主题数据处理
|
|||
for key, _ := range virtualBase { |
|||
objStation.Data.PyhData[key] = objStation.Data.ThemeData[key] |
|||
objStation.Data.ThemeData[key] = 0.0 |
|||
} |
|||
} else { // 测点数据处理
|
|||
themeData, ok := objStation.Data.GetValidThemeData() |
|||
if !ok { |
|||
log.Printf("减基点计算失败,无有效的主题数据。%s\n", objStation.LogMsg()) |
|||
objStation.Data.PyhData = objStation.Data.ThemeData |
|||
objStation.Data.ThemeData = map[string]any{} |
|||
continue |
|||
} |
|||
|
|||
// validFields 有效的监测项,主题数据的 field 必须在 baseValidFields 中存在
|
|||
station_themeData_fields := objStation.Data.GetThemeFields() |
|||
validFields, ok := t.filterFields(baseValidFields, station_themeData_fields) |
|||
if !ok { |
|||
log.Printf("基点数据计算失败,主题数据的 field 必须在 baseValidFields 中存在;"+ |
|||
"baseValidFields:%v, station_themeData_fields:%v 。 %s\n", |
|||
baseValidFields, station_themeData_fields, baseStation.LogMsg()) |
|||
|
|||
objStation.Data.PyhData = objStation.Data.ThemeData |
|||
objStation.Data.ThemeData = map[string]any{} |
|||
continue |
|||
} |
|||
|
|||
// 减基点
|
|||
var resultData map[string]any |
|||
for _, field := range validFields { |
|||
resultData[field] = virtualBase[field] - themeData[field] |
|||
} |
|||
objStation.Data.PyhData = objStation.Data.ThemeData |
|||
objStation.Data.ThemeData = resultData |
|||
} |
|||
} // for t.stationGroup.Items
|
|||
} |
|||
|
|||
return resultStations |
|||
} |
|||
|
|||
// calcVirtualSettlementBase 计算虚拟基点
|
|||
// Result = Base + (RefB - RefS)
|
|||
// Base - 测量系统基点
|
|||
// RefS - 远端参考系统测点
|
|||
// RefB - 远端参考系统基点
|
|||
func (t *CalcTask) calcVirtualSettlementBase(item common_models.GroupItem, fields []string) map[string]float64 { |
|||
baseStation := t.stationMap[item.StationId] |
|||
data, ok := baseStation.Data.GetValidThemeData() |
|||
if !ok { |
|||
log.Printf("基点无数据。%s\n", baseStation.LogMsg()) |
|||
return make(map[string]float64) |
|||
} |
|||
|
|||
// SubItem 为 nil,表示未参照任何分组,虚拟基点值等于自己
|
|||
if item.SubItems == nil { |
|||
result := make(map[string]float64) |
|||
for _, f := range fields { |
|||
result[f] = data[f] |
|||
} |
|||
return result |
|||
} |
|||
|
|||
logMsg, _ := json.Marshal(item) |
|||
log.Printf("级联分组:%s\n", string(logMsg)) |
|||
refS := t.calcVirtualSettlementBase(item.SubItems[settlementParam.Ref_point], fields) |
|||
refB := t.calcVirtualSettlementBase(item.SubItems[settlementParam.Ref_base], fields) |
|||
if len(refS) == 0 || len(refB) == 0 { |
|||
return make(map[string]float64) |
|||
} |
|||
|
|||
result := make(map[string]float64) |
|||
for _, f := range fields { |
|||
// 虚拟基点 = 当前分组的基点测量值 + 参考点沉降
|
|||
result[f] = data[f] + (refB[f] - refS[f]) |
|||
} |
|||
return result |
|||
} |
|||
|
|||
// filterFields 过滤有效的数据项(交集操作)
|
|||
func (t *CalcTask) filterFields(arrA []string, arrB []string) ([]string, bool) { |
|||
setA := make(map[string]bool) |
|||
for _, a := range arrA { |
|||
setA[a] = true |
|||
} |
|||
|
|||
var result []string |
|||
for _, b := range arrB { |
|||
if setA[b] { |
|||
result = append(result, b) |
|||
} |
|||
} |
|||
|
|||
return result, len(result) > 0 |
|||
} |
@ -0,0 +1,21 @@ |
|||
package group |
|||
|
|||
import ( |
|||
"fmt" |
|||
) |
|||
|
|||
// StationCalcTaskKey 测点计算任务KEY
|
|||
type StationCalcTaskKey struct { |
|||
StationId int |
|||
TaskId string |
|||
} |
|||
|
|||
// GroupCalcTaskKey 分组计算任务KEY
|
|||
type GroupCalcTaskKey struct { |
|||
GroupId int |
|||
TaskId string |
|||
} |
|||
|
|||
func (key *GroupCalcTaskKey) R() string { |
|||
return fmt.Sprintf("[GroupCalcTaskKey: %d %s] ", key.GroupId, key.TaskId) |
|||
} |
@ -0,0 +1,31 @@ |
|||
package group |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"github.com/stretchr/testify/assert" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
var configHelper = common_utils.NewConfigHelper("10.8.30.160:30379") |
|||
var group, _ = configHelper.GetStationGroup(36) |
|||
var processDataStrWSD_noFormula = ` |
|||
{"DeviceData":{"DeviceId":"ed0f1d94-49a9-415b-9336-f965a2b0a985","Name":"温湿度传感器m1","ThingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","StructId":3,"TaskId":"0e1c7d3d-257d-4763-a335-198aef0fc625","AcqTime":"2024-03-20T04:20:48.000125336+08:00","RealTime":"2024-03-20T04:20:48.000251942+08:00","ErrCode":0,"Raw":{"Temp":27.9,"humidy":13.5},"RawUnit":{"Temp":"℃","humidy":"%"},"DeviceInfo":{"id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","name":"温湿度传感器m1","structure":{"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","id":3,"name":"添加边坡","type":"边坡","orgId":1,"latitude":0,"longitude":0},"device_meta":{"id":"d5bf6f22-3d7a-4ab0-9043-1020e9516bcc","name":"温湿度传感器","model":"FS-BDS-WSD","properties":[],"capabilities":[{"capabilityCategoryId":3,"id":"d2add1b3-b21c-420b-82a4-e0c55ee3a019","name":"采集","properties":[{"category":"Output","name":"Temp","showName":"温度","unit":"℃"},{"category":"Output","name":"humidy","showName":"湿度","unit":"%"}]}]}},"DimensionId":"76c75371-bb9a-4f71-a25d-58adf7938296","DataType":""},"Stations":[{"Info":{"id":197,"name":"温湿度测点1","structure":3,"thingId":"5da9aa1b-05b7-4943-be57-dedb34f7a1bd","struct_name":"添加边坡","factor":2,"manual_data":false,"formula":0,"params_value":null,"Factor":{"id":2,"name":"环境温湿度","protoCode":"1002","protoName":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}],"units":{"humidity":"%RH","temperature":"℃"}},"proto":"1002","Proto":{"code":"1002","name":"温湿度","items":[{"id":3,"name":"温度","field_name":"temperature","unit_name":"℃","precision":0},{"id":4,"name":"湿度","field_name":"humidity","unit_name":"%RH","precision":0}]},"Devices":[{"formula_id":0,"params":{},"iota_device_id":"ed0f1d94-49a9-415b-9336-f965a2b0a985","iota_device_serial":0,"FormulaInfo":{"id":0,"expression":"","params":null,"ioFields":{"input":null,"output":null},"type":""},"DeviceFactorProto":{"formula":0,"field_val":{"Temp":"temperature","humidy":"humidity"},"FieldValUnitK":null}}],"Labels":"{}","CombineInfo":""},"Data":{"DeviceCalcData":null,"ThemeData":null,"CollectTime":"0001-01-01T00:00:00Z","AlarmLevel":0},"Threshold":{"Items":[{"item":3,"field_name":"temperature","name":"温度","level":1,"lower":20,"upper":100000,"begin":null,"end":null},{"item":3,"field_name":"temperature","name":"温度","level":2,"lower":10,"upper":20,"begin":null,"end":null},{"item":4,"field_name":"humidity","name":"湿度","level":1,"lower":6,"upper":16,"begin":null,"end":null}]}}]}` |
|||
|
|||
func Test_CalcTask_CheckIntegrity(t *testing.T) { |
|||
calcTask := NewGroupCalcTask(&group, "5分钟周期", "1", time.Now()) |
|||
|
|||
pd := &common_models.ProcessData{} |
|||
json.Unmarshal([]byte(processDataStrWSD_noFormula), pd) |
|||
calcTask.AddStationData(pd.Stations[0]) |
|||
|
|||
isFull := calcTask.CheckIntegrity() |
|||
assert.True(t, isFull, "分组计算项不齐全") |
|||
} |
|||
|
|||
func Test_CalcTask_ToDump(t *testing.T) { |
|||
task := NewGroupCalcTask(&group, "5分钟周期", "1", time.Now()) |
|||
println(task.ToDump()) |
|||
} |
@ -0,0 +1,155 @@ |
|||
package group |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"log" |
|||
"node/stages" |
|||
"sync" |
|||
"time" |
|||
) |
|||
|
|||
var ( |
|||
configHelperInstance *common_utils.ConfigHelper |
|||
once sync.Once |
|||
mu sync.Mutex |
|||
) |
|||
|
|||
func GetConfigHelper() *common_utils.ConfigHelper { |
|||
once.Do(func() { |
|||
configYaml := configLoad.LoadConfig() |
|||
redisAdd := configYaml.GetString("redis.address") |
|||
configHelperInstance = common_utils.NewConfigHelper(redisAdd) |
|||
}) |
|||
return configHelperInstance |
|||
} |
|||
|
|||
type GroupCalc struct { |
|||
stage *stages.Stage |
|||
configHelper *common_utils.ConfigHelper |
|||
signCalc chan bool |
|||
calcTasks map[GroupCalcTaskKey]CalcTask |
|||
} |
|||
|
|||
func NewGroupCalc() *GroupCalc { |
|||
calcTaskManager := &GroupCalc{ |
|||
stage: stages.NewStage("测点分组计算"), |
|||
configHelper: GetConfigHelper(), |
|||
signCalc: make(chan bool), |
|||
calcTasks: map[GroupCalcTaskKey]CalcTask{}, |
|||
} |
|||
|
|||
// 处理超期任务
|
|||
//go calcTaskManager.onClearDueTask()
|
|||
// 添加到 etNode 处理环境,实现数据加工 (缓存group各分项的主题数据 -> 分组计算 -> 分组数据)
|
|||
calcTaskManager.stage.AddProcess(calcTaskManager.processData) |
|||
|
|||
return calcTaskManager |
|||
} |
|||
|
|||
func (gc *GroupCalc) GetStage() stages.Stage { |
|||
return *gc.stage |
|||
} |
|||
|
|||
// processData 的 stations 被改变了
|
|||
func (gc *GroupCalc) processData(inData *common_models.ProcessData) *common_models.ProcessData { |
|||
var resultStations []common_models.Station |
|||
for _, station := range inData.Stations { |
|||
calcedStations := gc.cacheAndCalc(&station, inData.DeviceData.DimensionId, inData.DeviceData.TaskId, inData.DeviceData.AcqTime) |
|||
log.Printf("ProcessData中的测点个数:%d 计算返回的测点个数: %d", len(inData.Stations), len(calcedStations)) |
|||
resultStations = append(resultStations, calcedStations...) |
|||
} |
|||
|
|||
// 返回处理后的数据
|
|||
inData.Stations = resultStations |
|||
return inData |
|||
} |
|||
|
|||
// cacheAndCalc 缓存和计算
|
|||
// station 测点
|
|||
// dimensionId 采集策略
|
|||
// taskId 一次周期采集任务
|
|||
// acqTime 采集时间
|
|||
func (gc *GroupCalc) cacheAndCalc(station *common_models.Station, dimensionId string, taskId string, acqTime time.Time) []common_models.Station { |
|||
sGroup := station.Info.Group |
|||
corrGroups := station.Info.CorrGroups |
|||
if sGroup.Id == 0 || corrGroups == nil || len(corrGroups) == 0 { |
|||
// 非分组测点
|
|||
return []common_models.Station{*station} |
|||
} |
|||
|
|||
var resultStations []common_models.Station |
|||
key := GroupCalcTaskKey{ |
|||
GroupId: sGroup.Id, |
|||
TaskId: taskId, |
|||
} |
|||
if calcTask, ok := gc.calcTasks[key]; ok { |
|||
// 添加元素
|
|||
calcTask.AddStationData(*station) |
|||
|
|||
// 分组计算
|
|||
if calcTask.CheckIntegrity() { |
|||
secs := calcTask.ElapsedSecs() |
|||
if secs > 10 { |
|||
log.Printf("[dataHandler] group calc wait %f秒, %s\n", secs, key.R()) |
|||
} |
|||
|
|||
calcedStations := calcTask.Calc() |
|||
if calcedStations != nil { |
|||
resultStations = append(resultStations, calcedStations...) |
|||
} |
|||
|
|||
delete(gc.calcTasks, key) |
|||
} |
|||
|
|||
} else { |
|||
// 不存在的计算任务:要取到首个元素的设备的采集策略(维度),以便后面获得过期时长
|
|||
task := NewGroupCalcTask(&sGroup, dimensionId, taskId, acqTime) |
|||
task.AddStationData(*station) |
|||
|
|||
if task.CheckIntegrity() { |
|||
calcedStations := task.Calc() |
|||
if calcedStations != nil { |
|||
resultStations = append(resultStations, calcedStations...) |
|||
} |
|||
} else { |
|||
task.SetTimeout() |
|||
gc.calcTasks[key] = *task |
|||
} |
|||
} |
|||
|
|||
return resultStations |
|||
} |
|||
|
|||
//func (gc *GroupCalc) clearDueTask() {
|
|||
// for key, task := range gc.calcTasks {
|
|||
// if task.IsTimeout() {
|
|||
// result := task.Calc()
|
|||
// if result != nil {
|
|||
// // TODO 处理完的数据要传递出去
|
|||
// station.Data.GroupData = result
|
|||
// }
|
|||
// // 不管计算是否成功,到期的任务都要清除
|
|||
// delete(gc.calcTasks, key)
|
|||
// log.Printf("[dataHandler] group timeout calcTask:%s\n", key.R())
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
|
|||
//func (gc *GroupCalc) injectGroupData(calcTaskKey string, result map[string]any) {
|
|||
// //
|
|||
//}
|
|||
|
|||
//// onClearDueTask 处理超期任务
|
|||
//func (gc *GroupCalc) onClearDueTask() {
|
|||
// for {
|
|||
// select {
|
|||
// case <-gc.signCalc:
|
|||
// case <-time.After(time.Second):
|
|||
// }
|
|||
//
|
|||
// // 过期任务处理
|
|||
// gc.clearDueTask()
|
|||
// }
|
|||
//}
|
@ -0,0 +1 @@ |
|||
package group |
@ -0,0 +1,17 @@ |
|||
package group |
|||
|
|||
import ( |
|||
"fmt" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func Test_GroupDueTask_s(t *testing.T) { |
|||
// 创建分组超时任务对象
|
|||
dt := NewBaseDueTask() |
|||
time.Sleep(time.Second * 5) |
|||
|
|||
fmt.Println("Is Timeout:", dt.IsTimeout()) |
|||
fmt.Println("Elapsed Seconds:", dt.ElapsedSecs()) |
|||
dt.SetTimeout() |
|||
} |
@ -0,0 +1,99 @@ |
|||
package group |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models/constant/iotaScheme" |
|||
"log" |
|||
"math" |
|||
"time" |
|||
) |
|||
|
|||
const ( |
|||
data_active_expire_sec int = 900 |
|||
data_report_expire_sec int = 3600 |
|||
) |
|||
|
|||
// TimeStrategy 测点组合计算 超时时间判断策略
|
|||
type TimeStrategy struct { |
|||
activeExpiredSecs int |
|||
reportExpiredSecs int |
|||
} |
|||
|
|||
func DefaultExpire() int { |
|||
return data_report_expire_sec |
|||
} |
|||
|
|||
// FromDimension 返回超期时间(s)
|
|||
func FromDimension(dimension string) int { |
|||
if dimension == "" { |
|||
return DefaultExpire() |
|||
} |
|||
|
|||
scheme, err := GetConfigHelper().GetIotaScheme(dimension) |
|||
if err != nil { |
|||
log.Printf("Get IOTA Scheme ERR:%v", err) |
|||
} |
|||
if scheme.Mode == iotaScheme.ModeResponse { |
|||
return int(math.Min(float64(data_active_expire_sec), float64(scheme.IntervalSecs()))) |
|||
} |
|||
|
|||
return DefaultExpire() |
|||
} |
|||
|
|||
type IDueTask interface { |
|||
// 设置超期时长
|
|||
SetTimeout() int |
|||
} |
|||
type BaseDueTask struct { |
|||
// 数据注入时间(任务创建时间)
|
|||
InjectTime time.Time |
|||
// 超期时间
|
|||
DeadlineTime time.Time |
|||
} |
|||
|
|||
func NewBaseDueTask() *BaseDueTask { |
|||
return &BaseDueTask{ |
|||
InjectTime: time.Now(), |
|||
} |
|||
} |
|||
|
|||
// IsTimeout 是否超期
|
|||
func (t *BaseDueTask) IsTimeout() bool { |
|||
return !t.DeadlineTime.IsZero() && t.DeadlineTime.Before(time.Now()) |
|||
} |
|||
|
|||
// ElapsedSecs 当前流逝时间
|
|||
func (t *BaseDueTask) ElapsedSecs() float64 { |
|||
return time.Since(t.InjectTime).Seconds() |
|||
} |
|||
|
|||
// SetDeadLineTime 设置超期时间线
|
|||
func (t *BaseDueTask) SetDeadLineTime(secs int) { |
|||
t.DeadlineTime = t.InjectTime.Add(time.Second * time.Duration(secs)) |
|||
} |
|||
|
|||
func (t *BaseDueTask) SetTimeout() int { |
|||
return 0 |
|||
} |
|||
|
|||
//// GroupDueTask 分组超时任务
|
|||
//type GroupDueTask struct {
|
|||
// *BaseDueTask
|
|||
//}
|
|||
//
|
|||
//func NewGroupDueTask() *GroupDueTask {
|
|||
// return &GroupDueTask{
|
|||
// BaseDueTask: NewBaseDueTask(),
|
|||
// }
|
|||
//}
|
|||
//
|
|||
//func (c *GroupDueTask) SetTimeout() int {
|
|||
// return 0
|
|||
//}
|
|||
//
|
|||
//func main() {
|
|||
// // 创建分组超时任务对象
|
|||
// dt := NewGroupDueTask()
|
|||
// fmt.Println("Is Timeout:", dt.IsTimeout())
|
|||
// fmt.Println("Elapsed Seconds:", dt.ElapsedSecs())
|
|||
// dt.SetTimeout()
|
|||
//}
|
@ -0,0 +1,22 @@ |
|||
package et_calc |
|||
|
|||
import "strings" |
|||
|
|||
func unitTrans(data map[string]float64, inUnits, outUnits map[string]string) { |
|||
for kName, _ := range data { |
|||
inUnit, ok := inUnits[kName] |
|||
outUnit, ok2 := outUnits[kName] |
|||
if strings.EqualFold(inUnit, outUnit) || !ok || !ok2 { |
|||
continue |
|||
} else { |
|||
data[kName] *= getCoef(inUnit, outUnit) |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
func getCoef(in, out string) float64 { |
|||
k := 1.0 |
|||
|
|||
return k |
|||
} |
@ -0,0 +1,2 @@ |
|||
module et_print |
|||
go 1.22.0 |
@ -0,0 +1,31 @@ |
|||
package et_print |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
"node/stages" |
|||
) |
|||
|
|||
type PrintHandler struct { |
|||
stage *stages.Stage |
|||
} |
|||
|
|||
func (the *PrintHandler) GetStage() stages.Stage { |
|||
return *the.stage |
|||
} |
|||
|
|||
func NewPrintHandler() *PrintHandler { |
|||
the := &PrintHandler{ |
|||
stage: stages.NewStage("测试打印"), |
|||
} |
|||
|
|||
the.stage.AddProcess(the.print) |
|||
return the |
|||
} |
|||
|
|||
func (the *PrintHandler) print(p *common_models.ProcessData) *common_models.ProcessData { |
|||
|
|||
log.Printf("处理设备[%s]数据", p.DeviceData.Name) |
|||
|
|||
return p |
|||
} |
@ -0,0 +1,84 @@ |
|||
package et_prometheus_exporter |
|||
|
|||
import ( |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"github.com/prometheus/client_golang/prometheus" |
|||
"github.com/prometheus/client_golang/prometheus/promhttp" |
|||
"log" |
|||
"net/http" |
|||
"sync" |
|||
"time" |
|||
) |
|||
|
|||
// 定义自定义指标
|
|||
var ( |
|||
once sync.Once |
|||
//设备数据量统计
|
|||
devDataCounter = prometheus.NewCounterVec(prometheus.CounterOpts{ |
|||
Name: "device_data_count", |
|||
Help: "单次有效运行时间段内,设备数据量实时统计,累计有效数据条数", |
|||
}, []string{"deviceId", "thingId", "timeStart"}) |
|||
timeStart = time.Now().Format("2006-01-02 15:04:05") |
|||
|
|||
//设备数据最后时间统计
|
|||
devLastDataTimeGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{ |
|||
Name: "device_last_time_unix", |
|||
Help: "设备数据最新时间戳", |
|||
}, []string{"deviceId", "thingId"}) |
|||
) |
|||
|
|||
func init() { |
|||
// 注册自定义指标
|
|||
prometheus.MustRegister(devLastDataTimeGauge) |
|||
prometheus.MustRegister(devDataCounter) |
|||
} |
|||
|
|||
type PrometheusExporter struct { |
|||
} |
|||
|
|||
func NewPrometheusExporter() PrometheusExporter { |
|||
run() |
|||
return PrometheusExporter{} |
|||
|
|||
} |
|||
|
|||
func run() { |
|||
once.Do(func() { |
|||
go apiRun() |
|||
}) |
|||
} |
|||
func (p *PrometheusExporter) OnIotaData2metricByPrometheus(data *common_models.IotaData) { |
|||
|
|||
if data.Data.Success() == false { |
|||
return |
|||
} |
|||
|
|||
//log.Printf("prometheusExporter 导出设备[%s]指标 ", data.DeviceId)
|
|||
deviceId := data.DeviceId |
|||
thingId := data.ThingId |
|||
|
|||
collectTime := data.TriggerTime.Unix() |
|||
devLastDataTimeGauge.WithLabelValues(deviceId, thingId).Set(float64(collectTime)) |
|||
devDataCounter.WithLabelValues(deviceId, thingId, timeStart).Inc() |
|||
} |
|||
|
|||
func apiRun() { |
|||
|
|||
// 设置仪表的值
|
|||
|
|||
// 暴露metrics
|
|||
port := configLoad.LoadConfig().GetInt32("prometheus.port") //8080
|
|||
http.Handle("/metrics", myMetrics{}) |
|||
log.Println(fmt.Sprintf("Beginning to Prometheus Exporter serve on port :%d", port)) |
|||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) |
|||
} |
|||
|
|||
type myMetrics struct { |
|||
} |
|||
|
|||
func (m myMetrics) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|||
log.Println("prometheus 抓取") |
|||
promhttp.Handler().ServeHTTP(w, r) |
|||
} |
@ -0,0 +1,21 @@ |
|||
package et_prometheus_exporter |
|||
|
|||
import ( |
|||
"fmt" |
|||
"log" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestApi(t *testing.T) { |
|||
apiRun() |
|||
log.Print("=====prometheus apiRun=======") |
|||
} |
|||
func doSomethings() { |
|||
deviceId := fmt.Sprintf("deviceId-%d", time.Now().Unix()) |
|||
thingId := "11111-1111" |
|||
|
|||
collectTime := time.Now().Unix() |
|||
devLastDataTimeGauge.WithLabelValues(deviceId, thingId).Set(float64(collectTime)) |
|||
devDataCounter.WithLabelValues(deviceId, thingId, timeStart).Inc() |
|||
} |
@ -0,0 +1,3 @@ |
|||
module et_prometheus_exporter |
|||
|
|||
go 1.22 |
@ -0,0 +1,69 @@ |
|||
module et_push |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
) |
|||
|
|||
require ( |
|||
github.com/IBM/sarama v1.43.0 // indirect |
|||
github.com/allegro/bigcache v1.2.1 // indirect |
|||
github.com/beorn7/perks v1.0.1 // indirect |
|||
github.com/cespare/xxhash/v2 v2.2.0 // indirect |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
|||
github.com/eapache/go-resiliency v1.6.0 // indirect |
|||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect |
|||
github.com/eapache/queue v1.1.0 // indirect |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect |
|||
github.com/eko/gocache/lib/v4 v4.1.5 // indirect |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 // indirect |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 // indirect |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/golang/mock v1.6.0 // indirect |
|||
github.com/golang/protobuf v1.5.3 // indirect |
|||
github.com/golang/snappy v0.0.4 // indirect |
|||
github.com/gorilla/websocket v1.5.0 // indirect |
|||
github.com/hashicorp/errwrap v1.0.0 // indirect |
|||
github.com/hashicorp/go-multierror v1.1.1 // indirect |
|||
github.com/hashicorp/go-uuid v1.0.3 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect |
|||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect |
|||
github.com/jcmturner/gofork v1.7.6 // indirect |
|||
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect |
|||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect |
|||
github.com/klauspost/compress v1.17.7 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/pierrec/lz4/v4 v4.1.21 // indirect |
|||
github.com/prometheus/client_golang v1.14.0 // indirect |
|||
github.com/prometheus/client_model v0.3.0 // indirect |
|||
github.com/prometheus/common v0.37.0 // indirect |
|||
github.com/prometheus/procfs v0.8.0 // indirect |
|||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect |
|||
github.com/redis/go-redis/v9 v9.5.1 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/crypto v0.19.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/net v0.21.0 // indirect |
|||
golang.org/x/sync v0.6.0 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
google.golang.org/protobuf v1.31.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,122 @@ |
|||
package et_push |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"gitea.anxinyun.cn/container/common_utils/kafkaHelper" |
|||
"log" |
|||
"node/stages" |
|||
"time" |
|||
) |
|||
|
|||
type dataOut struct { |
|||
topic string |
|||
Id int `json:"id"` |
|||
Name string `json:"name"` |
|||
Data map[string]any `json:"data"` |
|||
CollectTime time.Time `json:"collect_time"` |
|||
} |
|||
|
|||
type IPushClient interface { |
|||
Publish(topic string, messageBytes []byte) |
|||
} |
|||
|
|||
type PushHandler struct { |
|||
stage *stages.Stage |
|||
pushClients []IPushClient |
|||
dataQueue []dataOut |
|||
signBatch chan bool |
|||
batchCount int |
|||
} |
|||
|
|||
func (the *PushHandler) GetStage() stages.Stage { |
|||
return *the.stage |
|||
} |
|||
|
|||
func NewPushHandler() *PushHandler { |
|||
the := &PushHandler{ |
|||
stage: stages.NewStage("测点数据推送"), |
|||
signBatch: make(chan bool, 1), |
|||
batchCount: 500, |
|||
} |
|||
the.addClients() |
|||
go the.publishBatchMonitor() |
|||
the.stage.AddProcess(the.push) |
|||
return the |
|||
} |
|||
|
|||
func (the *PushHandler) addClients() { |
|||
mqttEnable := configLoad.LoadConfig().GetBool("push.mqtt.enable") |
|||
if mqttEnable { |
|||
mqttHost := configLoad.LoadConfig().GetString("push.mqtt.host") |
|||
mqttPort := configLoad.LoadConfig().GetInt("push.mqtt.port") //clientIdPrefix
|
|||
clientIdPrefix := configLoad.LoadConfig().GetString("push.mqtt.clientIdPrefix") |
|||
mq := common_utils.NewMqttHelper( |
|||
mqttHost, |
|||
mqttPort, |
|||
fmt.Sprintf("%s-%s", clientIdPrefix, time.Now().Format("20060102-150405")), |
|||
"", |
|||
"", |
|||
false, |
|||
) |
|||
the.pushClients = append(the.pushClients, mq) |
|||
} |
|||
kafkaEnable := configLoad.LoadConfig().GetBool("push.kafka.enable") |
|||
if kafkaEnable { |
|||
kafkaBrokers := configLoad.LoadConfig().GetStringSlice("push.kafka.brokers") |
|||
ka := kafkaHelper.NewKafkaAsyncProducer(kafkaBrokers) |
|||
the.pushClients = append(the.pushClients, ka) |
|||
} |
|||
} |
|||
func (the *PushHandler) push(p *common_models.ProcessData) *common_models.ProcessData { |
|||
if len(the.pushClients) == 0 { |
|||
return p |
|||
} |
|||
|
|||
for _, station := range p.Stations { |
|||
dataPush := dataOut{ |
|||
topic: fmt.Sprintf("etpush/%d/%d", station.Info.StructureId, station.Info.Id), |
|||
Id: station.Info.Id, |
|||
Name: station.Info.Name, |
|||
Data: station.Data.ThemeData, |
|||
CollectTime: station.Data.CollectTime, |
|||
} |
|||
the.dataQueue = append(the.dataQueue, dataPush) |
|||
} |
|||
if len(the.dataQueue) >= the.batchCount { |
|||
log.Printf("推送队列 len=%d > %d,触发 批信号", len(the.dataQueue), the.batchCount) |
|||
the.signBatch <- true |
|||
} |
|||
return p |
|||
} |
|||
|
|||
func (the *PushHandler) publish(dataArrayOut []dataOut) { |
|||
for i, client := range the.pushClients { |
|||
log.Printf("[client-%d]publish %d 条数据", i, len(dataArrayOut)) |
|||
for _, out := range dataArrayOut { |
|||
outBytes, _ := json.Marshal(out) |
|||
client.Publish(out.topic, outBytes) |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
func (the *PushHandler) publishBatchMonitor() { |
|||
for { |
|||
select { |
|||
case <-the.signBatch: |
|||
log.Printf("批推送信号,监控器收到") |
|||
case <-time.After(200 * time.Millisecond): |
|||
} |
|||
if len(the.dataQueue) > 0 { |
|||
log.Printf("推送队列长度=%d/%d", len(the.dataQueue), cap(the.dataQueue)) |
|||
count := len(the.dataQueue) |
|||
needPush := the.dataQueue[:count] |
|||
the.dataQueue = the.dataQueue[count:] |
|||
go the.publish(needPush) |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,58 @@ |
|||
module et_sink |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
) |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_calc v0.0.1 // indirect |
|||
github.com/allegro/bigcache v1.2.1 // indirect |
|||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect |
|||
github.com/beorn7/perks v1.0.1 // indirect |
|||
github.com/cespare/xxhash/v2 v2.2.0 // indirect |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect |
|||
github.com/eko/gocache/lib/v4 v4.1.5 // indirect |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 // indirect |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 // indirect |
|||
github.com/elastic/go-elasticsearch/v6 v6.8.10 // indirect |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/golang/mock v1.6.0 // indirect |
|||
github.com/golang/protobuf v1.5.3 // indirect |
|||
github.com/google/uuid v1.6.0 // indirect |
|||
github.com/gorilla/websocket v1.5.0 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/influxdata/influxdb-client-go/v2 v2.13.0 // indirect |
|||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/oapi-codegen/runtime v1.0.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/prometheus/client_golang v1.14.0 // indirect |
|||
github.com/prometheus/client_model v0.3.0 // indirect |
|||
github.com/prometheus/common v0.37.0 // indirect |
|||
github.com/prometheus/procfs v0.8.0 // indirect |
|||
github.com/redis/go-redis/v9 v9.5.1 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/net v0.21.0 // indirect |
|||
golang.org/x/sync v0.6.0 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
google.golang.org/protobuf v1.31.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,251 @@ |
|||
package et_sink |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"gitea.anxinyun.cn/container/common_utils/storage/storageDBs" |
|||
"log" |
|||
"node/stages" |
|||
"time" |
|||
) |
|||
|
|||
type SinkHandler struct { |
|||
stage *stages.Stage |
|||
storageConsumers []storageDBs.IStorageConsumer |
|||
dataQueueRaw []common_models.EsRaw |
|||
dataQueueVib []common_models.EsVbRaw |
|||
dataQueueTheme []common_models.EsTheme |
|||
dataQueueGroup []common_models.EsGroupTheme // 分组主题数据队列
|
|||
signBatch chan bool |
|||
batchCount int |
|||
} |
|||
|
|||
const defaultBatchCount = 1000 |
|||
|
|||
func NewSinkThemeHandler() *SinkHandler { |
|||
the := &SinkHandler{ |
|||
stage: stages.NewStage("Theme 数据存储"), |
|||
storageConsumers: storageDBs.LoadIStorageConsumer(), |
|||
dataQueueTheme: make([]common_models.EsTheme, 0), |
|||
signBatch: make(chan bool, 1), |
|||
batchCount: defaultBatchCount, |
|||
} |
|||
go the.dumpThemeBatchMonitor() |
|||
the.stage.AddProcess(the.sinkThemeToES) |
|||
|
|||
return the |
|||
} |
|||
|
|||
func NewSinkGroupHandler() *SinkHandler { |
|||
esAddresses := configLoad.LoadConfig().GetStringSlice("es.addresses") |
|||
log.Printf("es addresses: %v", esAddresses) |
|||
|
|||
the := &SinkHandler{ |
|||
stage: stages.NewStage("EsGroupTheme 数据存储"), |
|||
storageConsumers: storageDBs.LoadIStorageConsumer(), |
|||
dataQueueGroup: make([]common_models.EsGroupTheme, 0), |
|||
batchCount: defaultBatchCount, |
|||
} |
|||
|
|||
go the.onGroupData() |
|||
the.stage.AddProcess(the.sinkGroupDataToES) |
|||
|
|||
return the |
|||
} |
|||
|
|||
func NewSinkRawHandler() *SinkHandler { |
|||
esAddresses := configLoad.LoadConfig().GetStringSlice("es.addresses") |
|||
log.Printf("es addresses: %v", esAddresses) |
|||
the := &SinkHandler{ |
|||
stage: stages.NewStage("raws 数据存储"), |
|||
storageConsumers: storageDBs.LoadIStorageConsumer(), |
|||
dataQueueRaw: make([]common_models.EsRaw, 0), |
|||
batchCount: defaultBatchCount, |
|||
} |
|||
go the.dumpRawBatchMonitor() |
|||
the.stage.AddProcess(the.sinkRawData) |
|||
return the |
|||
} |
|||
|
|||
func (the *SinkHandler) GetStage() stages.Stage { |
|||
return *the.stage |
|||
} |
|||
|
|||
func (the *SinkHandler) sinkRawData(p *common_models.ProcessData) *common_models.ProcessData { |
|||
go the.sinkRawDataToES(p.DeviceData) |
|||
return p |
|||
} |
|||
func (the *SinkHandler) sinkRawDataToES(deviceData common_models.DeviceData) { |
|||
|
|||
switch deviceData.DataType { |
|||
case common_models.RawTypeVib: |
|||
vibData := deviceData.GetVibrationData() |
|||
vbRaws := common_models.EsVbRaw{ |
|||
StructId: deviceData.StructId, |
|||
IotaDeviceName: deviceData.Name, |
|||
Param: vibData.FormatParams(), |
|||
Data: map[string]any{"raw": vibData.Data}, |
|||
CollectTime: deviceData.AcqTime.Truncate(time.Millisecond), |
|||
IotaDevice: deviceData.DeviceId, |
|||
CreateTime: time.Now().Truncate(time.Millisecond), |
|||
} |
|||
the.dataQueueVib = append(the.dataQueueVib, vbRaws) |
|||
case common_models.RawTypeDiag: |
|||
default: |
|||
esRaws := common_models.EsRaw{ |
|||
StructId: deviceData.StructId, |
|||
IotaDeviceName: deviceData.Name, |
|||
Data: deviceData.Raw, |
|||
CollectTime: deviceData.AcqTime.Truncate(time.Millisecond), |
|||
Meta: deviceData.DeviceInfo.DeviceMeta.GetOutputProps(), |
|||
IotaDevice: deviceData.DeviceId, |
|||
CreateTime: time.Now().Truncate(time.Millisecond), |
|||
} |
|||
the.dataQueueRaw = append(the.dataQueueRaw, esRaws) |
|||
} |
|||
|
|||
if len(the.dataQueueRaw) >= the.batchCount || len(the.dataQueueVib) >= the.batchCount { |
|||
the.signBatch <- true |
|||
} |
|||
} |
|||
|
|||
func (the *SinkHandler) dumpRawBatchMonitor() { |
|||
for { |
|||
select { |
|||
case <-the.signBatch: |
|||
log.Printf("批存储信号raw,监控器收到") |
|||
case <-time.After(200 * time.Millisecond): |
|||
} |
|||
if len(the.dataQueueRaw) > 0 { |
|||
count := len(the.dataQueueRaw) |
|||
log.Printf("es写入dataQueueRaw数据 count====> %d", count) |
|||
needDump := the.dataQueueRaw[:count] |
|||
the.dataQueueRaw = the.dataQueueRaw[count:] |
|||
the.dumpRaws(needDump) |
|||
} |
|||
|
|||
if len(the.dataQueueVib) > 0 { |
|||
count := len(the.dataQueueVib) |
|||
log.Printf("es写入dataQueueVib数据 count====> %d", count) |
|||
needDump := the.dataQueueVib[:count] |
|||
the.dataQueueVib = the.dataQueueVib[count:] |
|||
the.dumpVibRaws(needDump) |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
func (the *SinkHandler) dumpRaws(esRaws []common_models.EsRaw) { |
|||
for i, consumer := range the.storageConsumers { |
|||
log.Printf("[consumer-%d]存储raw数据 %d 条", i, len(esRaws)) |
|||
go consumer.SaveRaw(esRaws) |
|||
} |
|||
} |
|||
func (the *SinkHandler) dumpVibRaws(esVbRaws []common_models.EsVbRaw) { |
|||
for i, consumer := range the.storageConsumers { |
|||
log.Printf("[consumer-%d]存储VbRaw数据 %d 条", i, len(esVbRaws)) |
|||
go consumer.SaveVib(esVbRaws) |
|||
} |
|||
} |
|||
|
|||
func (the *SinkHandler) dumpThemeBatchMonitor() { |
|||
for { |
|||
select { |
|||
case <-the.signBatch: |
|||
log.Printf("批存储信号Theme,监控器收到") |
|||
case <-time.After(200 * time.Millisecond): |
|||
} |
|||
if len(the.dataQueueTheme) > 0 { |
|||
count := len(the.dataQueueTheme) |
|||
needDump := the.dataQueueTheme[:count] |
|||
the.dataQueueTheme = the.dataQueueTheme[count:] |
|||
the.dumpThemes(needDump) |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
func (the *SinkHandler) sinkThemeToES(p *common_models.ProcessData) *common_models.ProcessData { |
|||
go the.sinkThemeData(p.Stations) |
|||
return p |
|||
} |
|||
func (the *SinkHandler) sinkThemeData(stations []common_models.Station) { |
|||
var EsThemes []common_models.EsTheme |
|||
for _, station := range stations { |
|||
esTheme := common_models.EsTheme{ |
|||
SensorName: station.Info.Name, |
|||
FactorName: station.Info.Factor.Name, |
|||
FactorProtoCode: station.Info.Proto.Code, |
|||
Data: station.Data.ThemeData, |
|||
FactorProtoName: station.Info.Proto.Name, |
|||
Factor: station.Info.FactorId, |
|||
CollectTime: station.Data.CollectTime.Truncate(time.Millisecond), |
|||
Sensor: station.Info.Id, |
|||
Structure: station.Info.StructureId, |
|||
IotaDevice: station.Info.GetDeviceIdArray(), |
|||
CreateTime: time.Now().Truncate(time.Millisecond), |
|||
} |
|||
EsThemes = append(EsThemes, esTheme) |
|||
} |
|||
the.dataQueueTheme = append(the.dataQueueTheme, EsThemes...) |
|||
if len(the.dataQueueTheme) >= the.batchCount { |
|||
the.signBatch <- true |
|||
} |
|||
} |
|||
func (the *SinkHandler) dumpThemes(esThemes []common_models.EsTheme) { |
|||
for i, consumer := range the.storageConsumers { |
|||
log.Printf("[consumer-%d]存储Theme数据 %d 条", i, len(esThemes)) |
|||
go consumer.SaveTheme(esThemes) |
|||
} |
|||
} |
|||
|
|||
// ******************* 分组主题数据存储 *********************
|
|||
// onGroupData 监听分组主题数据
|
|||
func (the *SinkHandler) onGroupData() { |
|||
for { |
|||
select { |
|||
case <-the.signBatch: |
|||
case <-time.After(200 * time.Millisecond): |
|||
} |
|||
|
|||
if len(the.dataQueueGroup) > 0 { |
|||
count := len(the.dataQueueGroup) |
|||
needDump := the.dataQueueGroup[:count] |
|||
the.dataQueueGroup = the.dataQueueGroup[count:] |
|||
the.dumpGroupThemes(needDump) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (the *SinkHandler) sinkGroupDataToES(p *common_models.ProcessData) *common_models.ProcessData { |
|||
go the.sinkGroupData(p.Stations) |
|||
return p |
|||
} |
|||
|
|||
func (the *SinkHandler) sinkGroupData(stations []common_models.Station) { |
|||
var EsThemes []common_models.EsGroupTheme |
|||
for _, station := range stations { |
|||
esTheme := common_models.EsGroupTheme{ |
|||
Structure: station.Info.StructureId, |
|||
GroupId: station.Info.Group.Id, |
|||
GroupName: station.Info.Group.Name, |
|||
Factor: station.Info.FactorId, |
|||
FactorName: station.Info.Factor.Name, |
|||
FactorProtoCode: station.Info.Proto.Code, |
|||
FactorProtoName: station.Info.Proto.Name, |
|||
Data: station.Data.PyhData, // 分组下所有测点的主题数据
|
|||
CollectTime: station.Data.CollectTime.Truncate(time.Millisecond), |
|||
CreateTime: time.Now().Truncate(time.Millisecond), |
|||
} |
|||
EsThemes = append(EsThemes, esTheme) |
|||
} |
|||
the.dataQueueGroup = append(the.dataQueueGroup, EsThemes...) |
|||
if len(the.dataQueueGroup) >= the.batchCount { |
|||
the.signBatch <- true |
|||
} |
|||
} |
|||
func (the *SinkHandler) dumpGroupThemes(esThemes []common_models.EsGroupTheme) { |
|||
for _, consumer := range the.storageConsumers { |
|||
consumer.SaveGroupTheme(esThemes) |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
go 1.22.0 |
|||
|
|||
use ( |
|||
et_prometheus_exporter |
|||
et_Info |
|||
et_calc |
|||
et_push |
|||
et_sink |
|||
master |
|||
node |
|||
et_analyze |
|||
et_cache |
|||
dataSource |
|||
et_print |
|||
containerApp |
|||
) |
@ -0,0 +1,25 @@ |
|||
package app |
|||
|
|||
import ( |
|||
"dataSource/kafka" |
|||
"log" |
|||
) |
|||
|
|||
func init() { |
|||
log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds) |
|||
} |
|||
func Start() { |
|||
// 启动 master 服务
|
|||
master := NewEtMaster() |
|||
go master.RegisterListen() |
|||
//等待node注册
|
|||
master.WaitNodeRegister() |
|||
println("=======") |
|||
|
|||
// -> 源数据
|
|||
kafkaDataSource := kafka.NewKafkaDataSource() |
|||
go kafkaDataSource.Producer() |
|||
|
|||
// 将源数据 -> 各类型节点处理
|
|||
master.DistributeData(kafkaDataSource.DataChannels) |
|||
} |
@ -0,0 +1,318 @@ |
|||
package app |
|||
|
|||
import ( |
|||
"dataSource" |
|||
"encoding/gob" |
|||
"errors" |
|||
"et_prometheus_exporter" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"log" |
|||
"net" |
|||
"net/rpc" |
|||
"sort" |
|||
"time" |
|||
) |
|||
|
|||
func NewEtMaster() EtMaster { |
|||
master := EtMaster{ |
|||
exporter: et_prometheus_exporter.NewPrometheusExporter(), |
|||
sleepCH: make(chan bool, 1), |
|||
} |
|||
return master |
|||
} |
|||
|
|||
type EtMaster struct { |
|||
nodeList []*NodeRpc |
|||
exporter et_prometheus_exporter.PrometheusExporter |
|||
sleepCH chan bool |
|||
} |
|||
|
|||
type NodeRpc struct { |
|||
args *common_models.NodeArgs // 注册节点参数:RPC服务名为 master, 服务方法 NodeRegister 的输入参数
|
|||
resultCH chan bool // 注册节点参数:RPC服务名为 master, 服务方法 NodeRegister 的输出结果
|
|||
client *rpc.Client |
|||
} |
|||
|
|||
// RegisterListen 启动 master RPC服务
|
|||
func (the *EtMaster) RegisterListen() { |
|||
//监听
|
|||
err := rpc.RegisterName("master", the) |
|||
if err != nil { |
|||
log.Println("master 提供注册服务异常") |
|||
return |
|||
} |
|||
|
|||
port := configLoad.LoadConfig().GetUint16("master.port") |
|||
|
|||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) |
|||
if err != nil { |
|||
log.Panic("master 启动 node服务注册功能异常") |
|||
} |
|||
log.Printf("master 启动 node服务注册功能 :%d", port) |
|||
for { |
|||
//log.Println("master 监听新注册链接")
|
|||
conn, err := listener.Accept() |
|||
if err != nil { |
|||
log.Println("master rpc Accept异常") |
|||
} |
|||
log.Printf("master Accept注册链接 from node[%s]", conn.RemoteAddr()) |
|||
go rpc.ServeConn(conn) |
|||
} |
|||
} |
|||
|
|||
// NodeRegister 是 RPC 服务方法,由 et_node 远程调用
|
|||
func (the *EtMaster) NodeRegister(nodeArgs *common_models.NodeArgs, replay *bool) error { |
|||
node := &NodeRpc{ |
|||
args: nodeArgs, |
|||
resultCH: make(chan bool, 1), |
|||
client: nil, |
|||
} |
|||
//master 初始化 node client
|
|||
client, err := rpc.Dial("tcp", node.args.Addr) |
|||
if err != nil { |
|||
log.Printf("链接node失败-> node[%v]", node.args.Addr) |
|||
return err |
|||
} |
|||
node.client = client |
|||
|
|||
the.nodeList = append(the.nodeList, node) |
|||
log.Printf("node服务[%v] 注册成功", nodeArgs) |
|||
printNodesInfo(the.nodeList) |
|||
*replay = true |
|||
return nil |
|||
} |
|||
func (the *EtMaster) NodeHeart(nodeArgs *common_models.NodeArgs, replay *bool) error { |
|||
|
|||
isRegister := false |
|||
for _, nodeRpc := range the.nodeList { |
|||
if nodeRpc.args.Addr == nodeArgs.Addr { |
|||
isRegister = true |
|||
} |
|||
} |
|||
if !isRegister { |
|||
log.Printf("收到-未注册的node[%v] 心跳", nodeArgs) |
|||
*replay = false |
|||
return errors.New("未注册的node") |
|||
} |
|||
|
|||
log.Printf("收到-node[%v] 心跳", nodeArgs) |
|||
*replay = true |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// NodeUnRegister 节点RPC 注销
|
|||
func (the *EtMaster) NodeUnRegister(nodeArgs *common_models.NodeArgs, replay *bool) error { |
|||
|
|||
for i, node := range the.nodeList { |
|||
log.Printf("节点[%s] 注销", node.args.Addr) |
|||
if node.args.Addr == nodeArgs.Addr { |
|||
err := node.client.Close() |
|||
if err != nil { |
|||
log.Printf("节点[%s] client关闭异常 %s", node.args.Addr, err.Error()) |
|||
} |
|||
the.nodeList[i] = nil |
|||
} |
|||
} |
|||
the.nodesTidy() |
|||
log.Printf("node服务[%v] 注销成功", nodeArgs) |
|||
*replay = true |
|||
return nil |
|||
} |
|||
|
|||
func (the *EtMaster) nodesTidy() { |
|||
the.nodeList = updateNodeList(the.nodeList) |
|||
printNodesInfo(the.nodeList) |
|||
} |
|||
|
|||
func updateNodeList(nodes []*NodeRpc) []*NodeRpc { |
|||
var newNodes []*NodeRpc |
|||
for _, node := range nodes { |
|||
if node != nil && node.client != nil { |
|||
newNodes = append(newNodes, node) |
|||
} |
|||
} |
|||
return newNodes |
|||
} |
|||
func printNodesInfo(nodes []*NodeRpc) { |
|||
info := fmt.Sprintf("共[%d]个节点:\n ", len(nodes)) |
|||
for _, node := range nodes { |
|||
info += fmt.Sprintf("%s,%s\n", node.args.ID, node.args.Addr) |
|||
} |
|||
log.Println(info) |
|||
|
|||
} |
|||
|
|||
func (the *EtMaster) WaitNodeRegister() { |
|||
log.Println("等待 node进行注册") |
|||
for { |
|||
if len(the.nodeList) > 0 { |
|||
break |
|||
} |
|||
time.Sleep(time.Second * 10) |
|||
} |
|||
} |
|||
func (the *EtMaster) ConnectNode() { |
|||
for i := range the.nodeList { |
|||
nodeAddr := the.nodeList[i].args.Addr |
|||
if the.nodeList[i].client == nil { |
|||
client, err := rpc.Dial("tcp", nodeAddr) |
|||
if err != nil { |
|||
log.Printf("链接node失败-> node[%v]", nodeAddr) |
|||
continue |
|||
} |
|||
the.nodeList[i].client = client |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (the *EtMaster) call_etNode(node *NodeRpc, args *common_models.IotaData) { |
|||
if node.client == nil { |
|||
log.Printf("node [%v] client=nil", node.args) |
|||
return |
|||
} |
|||
|
|||
the.exporter.OnIotaData2metricByPrometheus(args) |
|||
resultCH := make(chan bool) |
|||
go func() { |
|||
defer timeCost(node.args.ID, args.DeviceId, time.Now()) |
|||
var rpcResult bool |
|||
err := node.client.Call("etNode.Handler", args, &rpcResult) |
|||
if err != nil { |
|||
log.Printf("rpc 调用node, err:%s", err.Error()) |
|||
} |
|||
resultCH <- rpcResult |
|||
}() |
|||
|
|||
//等result
|
|||
var result bool |
|||
timeOut := 1000 * time.Millisecond |
|||
select { |
|||
case result = <-resultCH: |
|||
case <-time.After(timeOut): |
|||
log.Printf("node[%s]处理[%s|%s]超过 %v,超时", node.args.Addr, args.DeviceId, args.TriggerTime, timeOut) |
|||
result = false |
|||
} |
|||
log.Printf("node[%s]处理[%s|%s]结果=%v", node.args.Addr, args.DeviceId, args.TriggerTime, result) |
|||
if result == false { |
|||
//发送 stop 信号
|
|||
|
|||
the.sleepCH <- true |
|||
log.Println("=============================================") |
|||
log.Printf("node[%s]处理[%s|%s]异常,触发nodesTidy", node.args.Addr, args.DeviceId, args.TriggerTime) |
|||
time.Sleep(time.Second * 5) |
|||
node.client = nil |
|||
the.nodesTidy() |
|||
} |
|||
} |
|||
|
|||
func (the *EtMaster) call_aggNode(node *NodeRpc, args *common_models.AggData) { |
|||
if node.client == nil { |
|||
log.Printf("et node [%v] client=nil", node.args) |
|||
return |
|||
} |
|||
go func() { |
|||
var response bool |
|||
err := node.client.Call("aggNode.Handler", args, &response) |
|||
if err != nil { |
|||
log.Printf("rpc err:%s", err.Error()) |
|||
} |
|||
// 将 rpc 返回值写入 chan
|
|||
node.resultCH <- response |
|||
}() |
|||
|
|||
// 接收数据
|
|||
var result bool |
|||
select { |
|||
case result = <-node.resultCH: |
|||
case <-time.After(2 * time.Second): |
|||
log.Println("rpc [aggHandler.Handler] 调用超时,退出。") |
|||
result = false |
|||
} |
|||
log.Printf("agg node[%s]处理结果:%v", node.args.Addr, result) |
|||
} |
|||
|
|||
// DistributeData 分发源数据给各类型处理节点。
|
|||
// 通过不断从不同类型的数据通道中读取数据,并根据节点类型分发数据进行处理,可以实现数据的采集和分发功能。
|
|||
func (the *EtMaster) DistributeData(dataChannels *dataSource.DataChannels) { |
|||
|
|||
//数据类型注册
|
|||
gob.Register([]interface{}{}) |
|||
|
|||
for { |
|||
if len(the.nodeList) == 0 { |
|||
log.Printf("nodeList is empty!") |
|||
time.Sleep(time.Second * 10) |
|||
continue |
|||
} |
|||
|
|||
select { |
|||
case stopEnable := <-the.sleepCH: |
|||
if stopEnable { |
|||
stopTime := time.Second * 10 |
|||
log.Printf("node 处理积压,%v,master 暂停 %v", stopEnable, stopTime) |
|||
time.Sleep(stopTime) |
|||
} else { |
|||
log.Printf("node 处理积压,%v,不正常空数据", stopEnable) |
|||
} |
|||
default: |
|||
} |
|||
|
|||
select { |
|||
case args := <-dataChannels.RawDataChan: |
|||
the.notifyRawData("etNode", args) |
|||
case args := <-dataChannels.AggDataChan: |
|||
the.notifyAggData("etAgg", args) |
|||
} |
|||
} |
|||
} |
|||
func contains(arr []string, target string) bool { |
|||
for _, value := range arr { |
|||
if value == target { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
func (the *EtMaster) sortNodeListByThingCount() { |
|||
sort.Slice(the.nodeList, func(i, j int) bool { |
|||
return len(the.nodeList[i].args.ThingIds) < len(the.nodeList[j].args.ThingIds) |
|||
}) |
|||
} |
|||
func (the *EtMaster) notifyRawData(nodeType string, d common_models.IotaData) { |
|||
|
|||
isMatch := false |
|||
for _, nodeRpc := range the.nodeList { |
|||
if contains(nodeRpc.args.ThingIds, d.ThingId) { |
|||
isMatch = true |
|||
switch nodeRpc.args.NodeType { |
|||
case nodeType: |
|||
the.call_etNode(nodeRpc, &d) |
|||
} |
|||
} |
|||
} |
|||
//无匹配触发 reBalance
|
|||
if !isMatch { |
|||
if len(the.nodeList) > 0 { |
|||
the.sortNodeListByThingCount() |
|||
the.nodeList[0].args.ThingIds = append(the.nodeList[0].args.ThingIds, d.ThingId) |
|||
log.Printf("thingId:[%s] 分配到node:[%s]", d.ThingId, the.nodeList[0].args.Addr) |
|||
the.call_etNode(the.nodeList[0], &d) |
|||
} |
|||
} |
|||
} |
|||
func (the *EtMaster) notifyAggData(nodeType string, d common_models.AggData) { |
|||
for _, nodeRpc := range the.nodeList { |
|||
switch nodeRpc.args.NodeType { |
|||
case nodeType: |
|||
go the.call_aggNode(nodeRpc, &d) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func timeCost(nodeId, deviceId string, start time.Time) { |
|||
tc := time.Since(start) |
|||
log.Printf("调用node[%s],[%s]耗时 = %v", nodeId, deviceId, tc) |
|||
} |
@ -0,0 +1,31 @@ |
|||
module master |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
) |
|||
|
|||
require ( |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,74 @@ |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 h1:tnk0LS3UZqyZwc3e3X9oRtkutMr5VkDbkEFgeQiVBTo= |
|||
gitea.anxinyun.cn/container/common_models v0.0.7/go.mod h1:i+0toGVrtTNHf5lxLQp573UOljDqbiG2iCY8729L8fk= |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 h1:3j/qkZEv7mdDJJ+4hHy6A78cQLHenuJmv9NtPmr4H/8= |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7/go.mod h1:rnWO6guEoy+fLovZJRzmhFwyq3/zwiXyzV2qa3U3L58= |
|||
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= |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
|||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= |
|||
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/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= |
|||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= |
|||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= |
|||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= |
|||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= |
|||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= |
|||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= |
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= |
|||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= |
|||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= |
|||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= |
|||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= |
|||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= |
|||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= |
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
|||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= |
|||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
|||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= |
|||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= |
|||
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/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= |
|||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= |
|||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= |
|||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= |
|||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= |
|||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= |
|||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= |
|||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= |
|||
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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
|||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= |
|||
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= |
|||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= |
|||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= |
|||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= |
|||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= |
|||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= |
|||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= |
|||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
|||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= |
|||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= |
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= |
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
|||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= |
|||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= |
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
|||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= |
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
@ -0,0 +1,10 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"master/app" |
|||
) |
|||
|
|||
func main() { |
|||
app.Start() |
|||
println("over=======") |
|||
} |
@ -0,0 +1,204 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bytes" |
|||
"dataSource" |
|||
"encoding/gob" |
|||
"encoding/json" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
master2 "master/app" |
|||
"testing" |
|||
) |
|||
|
|||
func TestMaster(t *testing.T) { |
|||
log.Println("测试开始") |
|||
rawDataMsg := `{ |
|||
"userId": "ce2d7eb2-e56e-422e-8bbe-95dfa18e32f8", |
|||
"thingId": "f5c6998b-91fa-42ae-8330-c2df41dcb88e", |
|||
"dimensionId": "ac06cda4-5975-4c58-9df3-e530ad20ad5d", |
|||
"dimCapId": "a8d3cb51-93d9-4ece-a414-59eec73f3be3", |
|||
"capId": "1ab8db7c-e725-4119-9c09-6555bb04ce5d", |
|||
"deviceId": "19f5e41a-6ec6-4101-b620-be0b415eaef9", |
|||
"scheduleId": "4a2a3d8b-ba33-40cc-89d8-47f6675a252c", |
|||
"taskId": "96417a21-93f1-41ce-83ec-48ba3c103bc6", |
|||
"jobId": 1, |
|||
"jobRepeatId": 1, |
|||
"triggerTime": "2024-02-28T15:09:22.000351473+08:00", |
|||
"realTime": "2024-02-28T15:09:22.001142356+08:00", |
|||
"finishTime": "2024-02-28T15:09:29.582668593+08:00", |
|||
"seq": 0, |
|||
"released": false, |
|||
"data": { |
|||
"type": 1, |
|||
"data": { |
|||
"windspeed": 9, |
|||
"humidity": 13.5, |
|||
"temp": 23.9, |
|||
"noise": 9, |
|||
"pm10": 9, |
|||
"pm25": 9, |
|||
"winddir": [1.1,2.2] |
|||
}, |
|||
"result": { |
|||
"code": 5001, |
|||
"msg": "值超量程[-900,900]", |
|||
"detail": null, |
|||
"errTimes": 1, |
|||
"dropped": false |
|||
} |
|||
} |
|||
}` |
|||
|
|||
// 初始化各数据源的数据通道
|
|||
dataChannels := dataSource.InitChannels() |
|||
|
|||
rawData := new(common_models.IotaData) |
|||
err := json.Unmarshal([]byte(rawDataMsg), rawData) |
|||
if err != nil { |
|||
log.Panicf("测试异常%s", err.Error()) |
|||
} |
|||
|
|||
gob.Register([]interface{}{}) |
|||
|
|||
// 向 RawDataChan 写数据
|
|||
dataChannels.RawDataChan <- *rawData |
|||
|
|||
master := new(master2.EtMaster) |
|||
master.ConnectNode() |
|||
master.DistributeData(dataChannels) |
|||
} |
|||
|
|||
func TestJobEn(t *testing.T) { |
|||
|
|||
// 注册空接口类型
|
|||
gob.Register([]interface{}{}) |
|||
|
|||
// 创建一个包含空接口的切片
|
|||
data := []interface{}{1, "hello", true} |
|||
|
|||
// 编码数据
|
|||
var buf bytes.Buffer |
|||
enc := gob.NewEncoder(&buf) |
|||
err := enc.Encode(data) |
|||
if err != nil { |
|||
fmt.Println("编码错误:", err) |
|||
return |
|||
} |
|||
|
|||
// 解码数据
|
|||
dec := gob.NewDecoder(&buf) |
|||
var decodedData []interface{} |
|||
err = dec.Decode(&decodedData) |
|||
if err != nil { |
|||
fmt.Println("解码错误:", err) |
|||
return |
|||
} |
|||
|
|||
fmt.Println(decodedData) |
|||
|
|||
} |
|||
func TestJobEnMap(t *testing.T) { |
|||
|
|||
// 注册空接口类型
|
|||
//gob.Register([]interface{}{})
|
|||
|
|||
// 创建一个包含空接口的切片
|
|||
data := map[string]interface{}{"a": "aa", "b": []float64{1.2, 2.2}} |
|||
|
|||
// 编码数据
|
|||
var buf bytes.Buffer |
|||
enc := gob.NewEncoder(&buf) |
|||
err := enc.Encode(data) |
|||
if err != nil { |
|||
fmt.Println("编码错误:", err) |
|||
return |
|||
} |
|||
|
|||
// 解码数据
|
|||
dec := gob.NewDecoder(&buf) |
|||
var decodedData map[string]interface{} |
|||
err = dec.Decode(&decodedData) |
|||
if err != nil { |
|||
fmt.Println("解码错误:", err) |
|||
return |
|||
} |
|||
|
|||
fmt.Println(decodedData) |
|||
|
|||
} |
|||
|
|||
func TestJobEnRawData(t *testing.T) { |
|||
// 创建一个包含空接口的切片
|
|||
//data := common_models.ThemeData{
|
|||
// Type: 0,
|
|||
// ThemeData: map[string]interface{}{"a": 1, "b": []float64{1.1, 2.2}},
|
|||
// Result: struct {
|
|||
// Code int `json:"code"`
|
|||
// Msg string `json:"msg"`
|
|||
// Detail string `json:"detail"`
|
|||
// ErrTimes int `json:"errTimes"`
|
|||
// Dropped bool `json:"dropped"`
|
|||
// }{},
|
|||
//}
|
|||
|
|||
jsonStr := `{ |
|||
"type": 1, |
|||
"data": { |
|||
"windspeed": 9, |
|||
"physicalvalue": [1.1,2.2] |
|||
}, |
|||
"result": { |
|||
"code": 5001, |
|||
"msg": "值超量程[-900,900]", |
|||
"detail": null, |
|||
"errTimes": 1, |
|||
"dropped": false |
|||
} |
|||
} |
|||
` |
|||
data2 := &common_models.Data{} |
|||
err := json.Unmarshal([]byte(jsonStr), &data2) |
|||
s, e := json.Marshal(data2.Data) |
|||
if e != nil { |
|||
return |
|||
} |
|||
println(string(s)) |
|||
if err != nil { |
|||
return |
|||
} |
|||
// 编码数据
|
|||
|
|||
gob.Register([]interface{}{}) // 注册空接口类型
|
|||
var buf bytes.Buffer |
|||
enc := gob.NewEncoder(&buf) |
|||
err = enc.Encode(data2) |
|||
if err != nil { |
|||
fmt.Println("编码错误:", err) |
|||
return |
|||
} |
|||
|
|||
// 解码数据
|
|||
gob.Register([]interface{}{}) |
|||
dec := gob.NewDecoder(&buf) |
|||
var decodedData common_models.Data |
|||
err = dec.Decode(&decodedData) |
|||
if err != nil { |
|||
fmt.Println("解码错误:", err) |
|||
return |
|||
} |
|||
|
|||
fmt.Println(decodedData) |
|||
|
|||
} |
|||
|
|||
func TestCH(t *testing.T) { |
|||
resultCH := make(chan bool) |
|||
log.Printf("写入数据1") |
|||
resultCH <- true |
|||
log.Printf("写入数据2") |
|||
resultCH <- false |
|||
log.Printf("写入数据3") |
|||
resultCH <- true |
|||
} |
@ -0,0 +1,53 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
"node/agg_worker" |
|||
"strings" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func Test_AggNodeHandler(t *testing.T) { |
|||
// 2024-04-08T11:01:48.001+08:00
|
|||
// TODO 聚集数据中时间:"date": "2024-04-19T01:01:59.999+0000"
|
|||
aggDataMSG := `{ |
|||
"date": "2024-04-19T01:01:59.999+0000", |
|||
"sensorId": 106, |
|||
"structId": 1, |
|||
"factorId": 11, |
|||
"aggTypeId": 2006, |
|||
"aggMethodId": 3004, |
|||
"agg": { |
|||
"strain": -16.399999618530273 |
|||
}, |
|||
"changed": { |
|||
"strain": -0.7999992370605469 |
|||
} |
|||
}` |
|||
// aggData 中的时间为UTC格式 2024-04-19T01:10:59.999+0000,
|
|||
// 在进行 json.Unmarshal() 时报错
|
|||
// 解决方案:先将 +0000 -> +00:00,然后再将 UTC 时间转换为 +08:00 时区时间
|
|||
|
|||
// 将 "+0000" 替换为 "+00:00"
|
|||
replacedStr := strings.Replace(aggDataMSG, "+0000", "+00:00", 1) |
|||
|
|||
aggData := &common_models.AggData{} |
|||
err := json.Unmarshal([]byte(replacedStr), &aggData) |
|||
if err != nil { |
|||
log.Panicf("测试异常%s", err.Error()) |
|||
} |
|||
|
|||
// 将 UTC 时间加上8小时得到中国的本地时间
|
|||
aggData.Date = aggData.Date.Add(8 * time.Hour) |
|||
|
|||
nodeWorker := agg_worker.NewAggWorker() |
|||
//nodeWorker.RegisterToMaster() 需要启动 master
|
|||
|
|||
log.Println("测试开始") |
|||
nodeWorker.ConsumerProcess(aggData) |
|||
time.Sleep(time.Second * 10) |
|||
log.Println("测试结束") |
|||
} |
@ -0,0 +1,85 @@ |
|||
package agg_worker |
|||
|
|||
import ( |
|||
"et_analyze" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"github.com/google/uuid" |
|||
"log" |
|||
"net/rpc" |
|||
"node/et_worker/et_recv" |
|||
"os" |
|||
"time" |
|||
) |
|||
|
|||
type AggNode struct { |
|||
recvDataHandler *et_recv.RecvDataHanler |
|||
} |
|||
|
|||
func NewAggWorker() *AggNode { |
|||
return &AggNode{ |
|||
recvDataHandler: et_recv.NewRecvDataHanler(), |
|||
} |
|||
} |
|||
|
|||
// Handler 是 RPC 接口,由 master 远程调用
|
|||
func (the *AggNode) Handler(aggData common_models.AggData, replay *bool) error { |
|||
*replay = true |
|||
err := the.ConsumerProcess(&aggData) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// ConsumerProcess 处理阈值判断业务
|
|||
func (the *AggNode) ConsumerProcess(aggData *common_models.AggData) error { |
|||
aggHandler := et_analyze.NewAggThresholdHandler() |
|||
aggHandler.ProcessData(aggData) |
|||
log.Printf("rpc聚集阈值分析[%d]-time[%s]-[%v]", aggData.SensorId, aggData.Date, aggData.Agg) |
|||
return nil |
|||
} |
|||
|
|||
// RegisterToMaster 调用 master 发布的RPC服务方法 master.NodeRegister
|
|||
func (the *AggNode) RegisterToMaster() { |
|||
connectCount := 0 |
|||
for { |
|||
connectCount++ |
|||
if connectCount > 3 { |
|||
log.Printf("RegisterToMaster 失败 超过%d次,准备退出", connectCount-1) |
|||
time.Sleep(time.Second * 10) |
|||
os.Exit(1) |
|||
} |
|||
masterAddr := os.Getenv("masterAddr") |
|||
if masterAddr == "" { |
|||
masterAddr = "127.0.0.1:50000" |
|||
} |
|||
|
|||
time.Sleep(time.Second * 1) |
|||
master, err := rpc.Dial("tcp", masterAddr) |
|||
if err != nil { |
|||
log.Printf("链接失败-> node[%s]", masterAddr) |
|||
continue |
|||
} |
|||
|
|||
//todo 获取node自己地址
|
|||
nodeAddr := "127.0.0.1:40001" |
|||
status := `{"health_status":"healthy","load_average":{"1_min":0.75,"5_min":1.2,"15_min":0.9},"availability":"available","last_check_time":"2022-01-01T12:00:00Z"}` |
|||
resources := `{"cpu":{"cores":4,"usage":"50%","temperature":"60°C"},"memory":{"total":"8GB","used":"4GB","available":"4GB"},"storage":{"total":"256GB","used":"100GB","available":"156GB"}}` |
|||
nodeArgs := &common_models.NodeArgs{ |
|||
ID: uuid.New().String(), |
|||
NodeType: "aggNode", |
|||
Status: status, |
|||
Resources: resources, |
|||
Addr: nodeAddr, |
|||
ThingIds: []string{}, |
|||
} |
|||
|
|||
var result bool |
|||
err = master.Call("master.NodeRegister", &nodeArgs, &result) |
|||
if err != nil { |
|||
log.Printf("node[%s]注册到master[%s]异常:%v", masterAddr, nodeAddr, result) |
|||
continue |
|||
} |
|||
break |
|||
} |
|||
} |
@ -0,0 +1,121 @@ |
|||
package app |
|||
|
|||
import ( |
|||
"encoding/gob" |
|||
"et_Info" |
|||
"et_analyze" |
|||
"et_cache" |
|||
"et_calc" |
|||
"et_print" |
|||
"et_push" |
|||
"et_sink" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"log" |
|||
"net" |
|||
"net/rpc" |
|||
"node/stages" |
|||
"time" |
|||
) |
|||
|
|||
func init() { |
|||
log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds) |
|||
} |
|||
|
|||
func Start() { |
|||
// etNode 注册
|
|||
nodeWorker := NewEtWorker() |
|||
// etNode 数据后处理环节
|
|||
nodeStageManage := stages.NewStageManager() |
|||
nodeStageManage.AddSource(nodeWorker.ch) |
|||
//add 业务环节
|
|||
nodeStageManage = addWorkStages(nodeStageManage) |
|||
|
|||
//add 测试环节
|
|||
//nodeStageManage = addTestPrintStages(nodeStageManage)
|
|||
|
|||
// 启动 etNode 处理
|
|||
nodeStageManage.Run() |
|||
|
|||
gob.Register([]interface{}{}) |
|||
err := rpc.RegisterName("etNode", nodeWorker) |
|||
if err != nil { |
|||
log.Panicf("注册 etNode rpc 异常") |
|||
} |
|||
go nodeSerRpcListen() |
|||
|
|||
// aggNode 注册,无数据后处理环节
|
|||
//aggWorker := agg_worker.NewAggWorker()
|
|||
//err1 := rpc.RegisterName("aggNode", aggWorker)
|
|||
//if err1 != nil {
|
|||
// log.Fatal("注册 aggNode rpc 异常", err1)
|
|||
//}
|
|||
//aggWorker.RegisterToMaster()
|
|||
|
|||
//后移注册流程,避免node启动异常的无效注册
|
|||
nodeWorker.RegisterToMaster() |
|||
|
|||
for { |
|||
time.Sleep(time.Hour) |
|||
} |
|||
} |
|||
func nodeSerRpcListen() { |
|||
port := configLoad.LoadConfig().GetUint16("node.port") |
|||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) |
|||
if err != nil { |
|||
log.Panicf("服务启动rpc 异常=%s", err.Error()) |
|||
} |
|||
log.Printf("服务监听=> :%d", port) |
|||
for { |
|||
conn, err := listener.Accept() |
|||
if err != nil { |
|||
log.Println("rpc Accept异常") |
|||
} |
|||
log.Printf("node 建立链接 from master[%s]", conn.RemoteAddr()) |
|||
go rpc.ServeConn(conn) |
|||
} |
|||
} |
|||
|
|||
func addWorkStages(nodeStageManage *stages.StageManager) *stages.StageManager { |
|||
// raws 数据存储
|
|||
sinkRawHandler := et_sink.NewSinkRawHandler() |
|||
nodeStageManage.AddStages(sinkRawHandler.GetStage()) |
|||
// 测点信息获取
|
|||
infoHandler := et_Info.NewInfoHandler() |
|||
nodeStageManage.AddStages(infoHandler.GetStage()) |
|||
// 单测点计算
|
|||
calcHandler := et_calc.NewCalcHandler() |
|||
nodeStageManage.AddStages(calcHandler.GetStage()) |
|||
|
|||
cacheHandler := et_cache.NewCacheHandler() |
|||
nodeStageManage.AddStages(cacheHandler.GetStage()) |
|||
|
|||
// Theme 数据存储
|
|||
sinkThemeHandler := et_sink.NewSinkThemeHandler() |
|||
nodeStageManage.AddStages(sinkThemeHandler.GetStage()) |
|||
// 测点主题数据的阈值分析
|
|||
stationAnalyzeHandler := et_analyze.NewThresholdHandler() |
|||
nodeStageManage.AddStages(stationAnalyzeHandler.GetStage()) |
|||
|
|||
// 测点分组计算
|
|||
//groupCalcHandler := group.NewGroupCalc()
|
|||
//nodeStageManage.AddStages(groupCalcHandler.GetStage())
|
|||
// EsGroupTheme 数据存储
|
|||
//sinkGroupHandler := et_sink.NewSinkGroupHandler()
|
|||
//nodeStageManage.AddStages(sinkGroupHandler.GetStage())
|
|||
|
|||
//// (阈值分析) 测点主题数据的阈值分析 + 分组计算后数据的阈值分析
|
|||
//groupAnalyzeHandler := et_analyze.NewThresholdHandler()
|
|||
//nodeStageManage.AddStages(groupAnalyzeHandler.GetStage())
|
|||
|
|||
publishHandler := et_push.NewPushHandler() |
|||
nodeStageManage.AddStages(publishHandler.GetStage()) |
|||
return nodeStageManage |
|||
} |
|||
|
|||
func addTestPrintStages(nodeStageManage *stages.StageManager) *stages.StageManager { |
|||
printHandler := et_print.NewPrintHandler() |
|||
nodeStageManage.AddStages(printHandler.GetStage()) |
|||
|
|||
return nodeStageManage |
|||
} |
@ -0,0 +1,222 @@ |
|||
package app |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"log" |
|||
"net/rpc" |
|||
"node/et_worker/et_recv" |
|||
"os" |
|||
"os/signal" |
|||
"syscall" |
|||
"time" |
|||
) |
|||
|
|||
type EtNode struct { |
|||
nodeInfo *common_models.NodeArgs |
|||
master *rpcMaster |
|||
ch chan *common_models.ProcessData |
|||
recvDataHandler *et_recv.RecvDataHanler |
|||
} |
|||
|
|||
type rpcMaster struct { |
|||
conn *rpc.Client |
|||
addr string |
|||
} |
|||
|
|||
const chSize = 2 |
|||
|
|||
func NewEtWorker() *EtNode { |
|||
node := &EtNode{ |
|||
ch: make(chan *common_models.ProcessData, chSize), |
|||
recvDataHandler: et_recv.NewRecvDataHanler(), |
|||
} |
|||
node.exitMonitor() |
|||
node.heartMonitor() |
|||
return node |
|||
} |
|||
|
|||
// Handler 是 RPC 服务方法,由 master 远程调用
|
|||
func (the *EtNode) Handler(iotaData common_models.IotaData, replay *bool) error { |
|||
*replay = true |
|||
err := the.ConsumerProcess(&iotaData) |
|||
if err != nil { |
|||
*replay = false |
|||
} |
|||
|
|||
return err |
|||
} |
|||
|
|||
// ConsumerProcess 将 IotaData -> ProcessData
|
|||
func (the *EtNode) ConsumerProcess(iotaData *common_models.IotaData) error { |
|||
deviceData, err := the.recvDataHandler.OnDataHandler(*iotaData) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
if deviceData == nil { |
|||
return nil |
|||
} |
|||
|
|||
log.Printf("rpc处理设备数据[%s]-time[%s]-[%v]", deviceData.DeviceId, deviceData.AcqTime, deviceData.Raw) |
|||
//log.Printf("rpc处理设备数据[%s]-time[%s]-data:%v", iotaData.DeviceId, iotaData.TriggerTime, iotaData.ThemeData.ThemeData)
|
|||
the.ch <- &common_models.ProcessData{ |
|||
DeviceData: *deviceData, |
|||
Stations: []common_models.Station{}, |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// 实现源接口
|
|||
func (the *EtNode) Process(ctx context.Context) (<-chan any, error) { |
|||
source := make(chan any, chSize) |
|||
go func() { |
|||
defer close(source) |
|||
for { |
|||
select { |
|||
case a := <-the.ch: |
|||
source <- a |
|||
log.Printf("存储数据=>source out,len=%d,%d", len(source), len(the.ch)) |
|||
case <-ctx.Done(): |
|||
log.Println("退出[source] EtNode.Process") |
|||
return |
|||
} |
|||
|
|||
} |
|||
}() |
|||
return source, nil |
|||
} |
|||
|
|||
// RegisterToMaster 调用 master 发布的RPC服务方法 master.NodeRegister
|
|||
func (the *EtNode) RegisterToMaster() { |
|||
maxCount := 3 |
|||
connectCount := 0 |
|||
for { |
|||
connectCount++ |
|||
if connectCount > maxCount { |
|||
log.Printf("RegisterToMaster 失败 超过%d次,准备退出", maxCount) |
|||
time.Sleep(time.Second * 10) |
|||
os.Exit(1) |
|||
} |
|||
masterAddr := loadMasterAddr() |
|||
masterConn, err := rpc.Dial("tcp", masterAddr) |
|||
if err != nil { |
|||
log.Printf("链接失败-> node[%s]", masterAddr) |
|||
time.Sleep(time.Second * 5) |
|||
continue |
|||
} |
|||
the.master = &rpcMaster{ |
|||
conn: masterConn, |
|||
addr: masterAddr, |
|||
} |
|||
time.Sleep(time.Millisecond * 200) |
|||
//获取node自己地址
|
|||
ipPrefix := configLoad.LoadConfig().GetString("node.hostIpPrefix") |
|||
ip4 := common_utils.ReadIP4WithPrefixFirst(ipPrefix) |
|||
hostName, err := os.Hostname() |
|||
log.Printf("node [%s] ip=%s", hostName, ip4) |
|||
port := configLoad.LoadConfig().GetUint16("node.port") |
|||
callNodeAddr := fmt.Sprintf("%s:%d", ip4, port) |
|||
|
|||
if the.nodeInfo == nil { |
|||
the.nodeInfo = &common_models.NodeArgs{ |
|||
ID: hostName + time.Now().Format("_20060102_150405"), |
|||
NodeType: "etNode", |
|||
Status: "", |
|||
Resources: "", |
|||
Addr: callNodeAddr, |
|||
ThingIds: []string{}, |
|||
} |
|||
} |
|||
|
|||
var result bool |
|||
err = the.master.conn.Call("master.NodeRegister", the.nodeInfo, &result) |
|||
if err != nil { |
|||
log.Printf("node[%s] 注册到 master[%s]异常:%v", the.nodeInfo.Addr, the.master.addr, result) |
|||
continue |
|||
} |
|||
break |
|||
} |
|||
} |
|||
func (the *EtNode) heartToMaster() { |
|||
maxCount := 3 |
|||
connectCount := 0 |
|||
reRegister := false |
|||
for { |
|||
connectCount++ |
|||
if connectCount > maxCount { |
|||
log.Printf("heartToMaster 失败 超过%d次", maxCount) |
|||
reRegister = true |
|||
break |
|||
} |
|||
var result bool |
|||
err := the.master.conn.Call("master.NodeHeart", the.nodeInfo, &result) |
|||
if err != nil { |
|||
log.Printf("node[%s] 心跳到 master[%s]异常:%v", the.nodeInfo.Addr, the.master.addr, result) |
|||
time.Sleep(time.Second * 5) |
|||
continue |
|||
} |
|||
break |
|||
} |
|||
if reRegister { //触发重新注册
|
|||
log.Printf("node[%s] 心跳失败触发-重新注册到 master[%s]", the.nodeInfo.Addr, the.master.addr) |
|||
the.RegisterToMaster() |
|||
} |
|||
} |
|||
|
|||
func (the *EtNode) UnRegisterToMaster() { |
|||
var result bool |
|||
if err := the.master.conn.Call("master.NodeUnRegister", the.nodeInfo, &result); err != nil { |
|||
log.Printf("node[%s] 注销到 master,异常:%v", the.nodeInfo.Addr, err.Error()) |
|||
} else { |
|||
log.Printf("node[%s] 注销到 master,结果:%v", the.nodeInfo.Addr, result) |
|||
} |
|||
} |
|||
func (the *EtNode) exitMonitor() { |
|||
go func() { |
|||
c := make(chan os.Signal, 1) |
|||
// 通过signal.Notify函数将信号通道c注册到系统相关的退出信号上
|
|||
// 这里使用了两个退出信号:syscall.SIGINT(Ctrl+C)和syscall.SIGTERM(系统发送的退出信号)
|
|||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) |
|||
// 阻塞等待接收信号
|
|||
s := <-c |
|||
log.Printf("接收到退出信号:%v,进行清理工作\n", s) |
|||
the.UnRegisterToMaster() |
|||
time.Sleep(3 * time.Second) |
|||
os.Exit(0) |
|||
}() |
|||
} |
|||
func (the *EtNode) heartMonitor() { |
|||
go func() { |
|||
ticker := time.NewTicker(time.Minute) |
|||
defer ticker.Stop() |
|||
for range ticker.C { |
|||
if the.master != nil { |
|||
log.Printf("node[%s] 心跳触发-> master[%s]", the.nodeInfo.Addr, the.master.addr) |
|||
the.heartToMaster() |
|||
} |
|||
} |
|||
|
|||
}() |
|||
|
|||
} |
|||
|
|||
// LoadCh test用
|
|||
func (the *EtNode) LoadCh() chan *common_models.ProcessData { |
|||
return the.ch |
|||
} |
|||
|
|||
func loadMasterAddr() string { |
|||
masterHost := configLoad.LoadConfig().GetString("node.remoteMasterHost") |
|||
masterPort := configLoad.LoadConfig().GetUint16("master.port") |
|||
if masterHost == "" { |
|||
masterHost = "127.0.0.1" |
|||
} |
|||
if masterPort == 0 { |
|||
masterPort = 50000 |
|||
} |
|||
return fmt.Sprintf("%s:%d", masterHost, masterPort) |
|||
} |
@ -0,0 +1,212 @@ |
|||
package et_recv |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"errors" |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils" |
|||
"gitea.anxinyun.cn/container/common_utils/configLoad" |
|||
"gitea.anxinyun.cn/container/common_utils/kafkaHelper" |
|||
"log" |
|||
"strconv" |
|||
"time" |
|||
) |
|||
|
|||
func Recover(deviceId string, structId int, alarmType string, time time.Time) { |
|||
alarm := common_models.AlarmMsg{ |
|||
MessageMode: common_models.Alarm_Mode_AutoElimination, |
|||
StructureId: structId, |
|||
StructureName: "", |
|||
SourceId: deviceId, |
|||
SourceName: "", |
|||
AlarmTypeCode: alarmType, |
|||
AlarmCode: "", |
|||
Content: "", |
|||
AcqTime: time, |
|||
SourceTypeId: 1, |
|||
Sponsor: "et.recv", |
|||
Extras: nil, |
|||
SubDevices: nil, |
|||
} |
|||
jsonOut, _ := json.Marshal(alarm) |
|||
brokers := configLoad.LoadConfig().GetStringSlice("kafka.brokers") |
|||
kafkaHelper.Send2Topic(brokers, "native_alarm", string(jsonOut)) |
|||
} |
|||
func AlarmToOut(deviceId string, structId int, alarmType string, time time.Time, subDevices []string) { |
|||
alarm := common_models.AlarmMsg{ |
|||
MessageMode: common_models.Alarm_Mode_Generation, |
|||
StructureId: structId, |
|||
StructureName: "", |
|||
SourceId: deviceId, |
|||
SourceName: "", |
|||
AlarmTypeCode: alarmType, |
|||
AlarmCode: "", |
|||
Content: "", |
|||
AcqTime: time, |
|||
SourceTypeId: common_models.Alarm_Source_Device, |
|||
Sponsor: common_models.Alarm_Sponsor_Recv, |
|||
Extras: nil, |
|||
SubDevices: subDevices, |
|||
} |
|||
jsonOut, _ := json.Marshal(alarm) |
|||
brokers := configLoad.LoadConfig().GetStringSlice("kafka.brokers") |
|||
kafkaHelper.Send2Topic(brokers, "native_alarm", string(jsonOut)) |
|||
} |
|||
func AlarmDtuToOut(device *common_models.DeviceInfo, alarmType, alarmCode, content string, time time.Time, subDevices []string) { |
|||
alarm := common_models.AlarmMsg{ |
|||
MessageMode: common_models.Alarm_Mode_Generation, |
|||
StructureId: device.Structure.Id, |
|||
StructureName: device.Structure.Name, |
|||
SourceId: device.Id, |
|||
SourceName: device.Name, |
|||
AlarmTypeCode: alarmType, |
|||
AlarmCode: alarmCode, |
|||
Content: content, |
|||
AcqTime: time, |
|||
SourceTypeId: common_models.Alarm_Source_DTU, |
|||
Sponsor: common_models.Alarm_Sponsor_Recv, |
|||
Extras: nil, |
|||
SubDevices: subDevices, |
|||
} |
|||
jsonOut, _ := json.Marshal(alarm) |
|||
brokers := configLoad.LoadConfig().GetStringSlice("kafka.brokers") |
|||
kafkaHelper.Send2Topic(brokers, "native_alarm", string(jsonOut)) |
|||
} |
|||
|
|||
type RecvDataHanler struct { |
|||
configHelper *common_utils.ConfigHelper |
|||
alarmCacheUtil *common_utils.AlarmCacheUtil |
|||
} |
|||
|
|||
func NewRecvDataHanler() *RecvDataHanler { |
|||
redisAddr := configLoad.LoadConfig().GetString("redis.address") |
|||
return &RecvDataHanler{ |
|||
configHelper: common_utils.NewConfigHelper(redisAddr), |
|||
alarmCacheUtil: common_utils.NewAlarmCacheUtil(), |
|||
} |
|||
} |
|||
|
|||
// OnDataHandler iota 数据处理
|
|||
func (the *RecvDataHanler) OnDataHandler(iotaData common_models.IotaData) (*common_models.DeviceData, error) { |
|||
configHelper := the.configHelper //common_utils.NewConfigHelper(common_utils.NewRedisHelper("", "192.168.31.128:30379"))
|
|||
|
|||
deviceInfo, err := configHelper.GetDeviceInfo(iotaData.DeviceId) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
if deviceInfo == nil { |
|||
errMsg := fmt.Sprintf("[%s] DeviceId not found in redis ", iotaData.DeviceId) |
|||
log.Printf(errMsg) |
|||
return nil, errors.New(errMsg) |
|||
} |
|||
//code := iotaData.ThemeData.Result.Code
|
|||
//taskId := iotaData.ReadTaskId()
|
|||
if iotaData.Data.Success() { |
|||
//数据恢复设备高告警
|
|||
if len(iotaData.Data.Data) == 0 { |
|||
log.Printf("[%s] empty data received", iotaData.DeviceId) |
|||
} |
|||
|
|||
toRecover := []string{ |
|||
common_models.Alarm_Type_Device_Status, |
|||
common_models.Alarm_Type_Timeout, |
|||
common_models.Alarm_Type_Data_Parse_Error, |
|||
common_models.Alarm_Type_Data_Interupt, |
|||
common_models.Alarm_Type_OutRange, |
|||
common_models.Alarm_Type_OutRange_Legacy} |
|||
alarmCacheUtil := common_utils.AlarmCacheUtil{} |
|||
affects := alarmCacheUtil.Rem(alarmCacheUtil.ALARM_SOURCE_DEVICE, iotaData.DeviceId, toRecover...) |
|||
if affects > 0 { |
|||
for _, alarmType := range toRecover { |
|||
Recover(deviceInfo.Id, deviceInfo.Structure.Id, alarmType, iotaData.TriggerTime) |
|||
} |
|||
} |
|||
} else { |
|||
var leafNodes = configHelper.GetSubDeviceNext(iotaData.DeviceId, iotaData.ThingId) |
|||
if len(leafNodes) > 0 { |
|||
//todo
|
|||
} |
|||
|
|||
//Key_alarm_code
|
|||
alarmTypeOpt, err := configHelper.GetAlarmCode(strconv.Itoa(iotaData.Data.Result.Code)) |
|||
if err == nil { |
|||
the.alarmCacheUtil.Add(the.alarmCacheUtil.ALARM_SOURCE_DEVICE, alarmTypeOpt.TypeCode) |
|||
AlarmToOut(deviceInfo.Id, deviceInfo.Structure.Id, alarmTypeOpt.TypeCode, iotaData.TriggerTime, leafNodes) |
|||
} |
|||
|
|||
if alarmTypeOpt.TypeCode == common_models.Alarm_Type_OutRange { |
|||
iotaData.Data.Result.Code = 0 |
|||
} |
|||
|
|||
} |
|||
|
|||
if iotaData.Data.Result.Code == 0 { |
|||
dataType := "" |
|||
if _dataType, ok := iotaData.Data.Data["_data_type"]; ok { |
|||
if v, ok := _dataType.(string); ok { |
|||
dataType = v |
|||
} |
|||
} |
|||
data := &common_models.DeviceData{ |
|||
DeviceId: iotaData.DeviceId, |
|||
Name: deviceInfo.Name, |
|||
ThingId: iotaData.ThingId, |
|||
StructId: deviceInfo.Structure.Id, |
|||
TaskId: iotaData.ReadTaskId(), |
|||
AcqTime: iotaData.TriggerTime, |
|||
RealTime: iotaData.RealTime, |
|||
ErrCode: 0, |
|||
Raw: iotaData.Data.Data, |
|||
DeviceInfo: *deviceInfo, |
|||
DimensionId: iotaData.DimensionId, |
|||
DataType: dataType, |
|||
} |
|||
return data, err |
|||
} |
|||
|
|||
return nil, err |
|||
} |
|||
|
|||
// OnAlarmHandler iota 告警处理
|
|||
func (the *RecvDataHanler) OnAlarmHandler(iotaAlarm common_models.IotaAlarm) { |
|||
configHelper := the.configHelper |
|||
deviceId := iotaAlarm.Labels.DeviceId |
|||
deviceInfo, err := configHelper.GetDeviceInfo(deviceId) |
|||
if err != nil { |
|||
return |
|||
} else { |
|||
switch iotaAlarm.Status { |
|||
//自动恢复
|
|||
case common_models.Iota_Alarm_Status_Resolved: |
|||
switch iotaAlarm.Labels.AlertName { |
|||
case common_models.Iota_Alarm_OutOfRange: |
|||
|
|||
case common_models.Iota_Alarm_LinkStatus: |
|||
Recover(deviceInfo.Id, deviceInfo.Structure.Id, common_models.Iota_Alarm_LinkStatus, iotaAlarm.StartsAt) |
|||
default: |
|||
log.Printf("%s not support alarm type: %s - %s - %s", iotaAlarm.R_(), |
|||
iotaAlarm.Labels.AlertName, iotaAlarm.Annotations.Summary, iotaAlarm.Annotations.Description) |
|||
} |
|||
case common_models.Iota_Alarm_Status_Firing: |
|||
switch iotaAlarm.Labels.AlertName { |
|||
case common_models.Iota_Alarm_OutOfRange: |
|||
|
|||
case common_models.Iota_Alarm_LinkStatus: |
|||
//dtu断线告警 需要查询下面所有子设备
|
|||
subDevices := configHelper.GetSubDeviceAll(iotaAlarm.Labels.DeviceId, iotaAlarm.Labels.ThingId) |
|||
AlarmDtuToOut( |
|||
deviceInfo, |
|||
common_models.Alarm_Type_Dtu_LinkStatus, |
|||
common_models.Alarm_Code_OffLine, |
|||
iotaAlarm.Annotations.Summary, |
|||
iotaAlarm.StartsAt, |
|||
subDevices) |
|||
default: |
|||
log.Printf("%s not support alarm type: %s - %s - %s", iotaAlarm.R_(), |
|||
iotaAlarm.Labels.AlertName, iotaAlarm.Annotations.Summary, iotaAlarm.Annotations.Description) |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,114 @@ |
|||
package et_worker |
|||
|
|||
import ( |
|||
"context" |
|||
"node/et_worker/processors" |
|||
"node/stages" |
|||
) |
|||
|
|||
type ProcessorManager struct { |
|||
source processors.ISource |
|||
sink processors.ISink |
|||
ps []processors.IProcessor |
|||
} |
|||
|
|||
func NewProcessorManager() *ProcessorManager { |
|||
return &ProcessorManager{} |
|||
} |
|||
|
|||
func (the *ProcessorManager) AddSource(source processors.ISource) { |
|||
the.source = source |
|||
} |
|||
|
|||
func (the *ProcessorManager) AddProcessor(processor processors.IProcessor) { |
|||
the.ps = append(the.ps, processor) |
|||
} |
|||
|
|||
func (the *ProcessorManager) AddSink(sink processors.ISink) { |
|||
the.sink = sink |
|||
} |
|||
|
|||
func (the *ProcessorManager) Run(ctx context.Context) error { |
|||
var err error |
|||
|
|||
//in, err := the.source.Process(ctx)
|
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
//stage1 := stages.NewStage("测点信息获取")
|
|||
stage2 := stages.NewStage("单测点计算") |
|||
out := stage2.StageRun() |
|||
for { |
|||
the.sink.Process(ctx, <-out) |
|||
} |
|||
|
|||
//stage1.AddProcess(Add1)
|
|||
//stage1.AddProcess(Add1000)
|
|||
// pipeline构建和执行
|
|||
//for data := range in {
|
|||
// for _, v := range the.ps {
|
|||
// data, err = v.Process(ctx, data)
|
|||
//
|
|||
// // 错误集中处理,这里暂跳过
|
|||
// if err != nil {
|
|||
// log.Printf("Process err %s\n", err)
|
|||
// break
|
|||
// }
|
|||
// }
|
|||
//
|
|||
// err := the.sink.Process(ctx, data)
|
|||
// if err != nil {
|
|||
// log.Printf("Sink err %s\n", err)
|
|||
// return nil
|
|||
// }
|
|||
//}
|
|||
|
|||
return nil |
|||
} |
|||
|
|||
//func (the *ProcessorManager) RunN(ctx context.Context, maxCnt int) error {
|
|||
// var err error
|
|||
//
|
|||
// in, err := the.source.Process(ctx)
|
|||
// if err != nil {
|
|||
// return err
|
|||
// }
|
|||
//
|
|||
// // pipeline构建和执行
|
|||
// syncProcess := func(data any) {
|
|||
// for _, v := range the.ps {
|
|||
// data, err = v.Process(ctx, data)
|
|||
//
|
|||
// // 错误集中处理,这里选择提前退出
|
|||
// if err != nil {
|
|||
// log.Printf("Process err %s\n", err)
|
|||
// return
|
|||
// }
|
|||
// }
|
|||
//
|
|||
// err := the.sink.Process(ctx, data)
|
|||
// if err != nil {
|
|||
// log.Printf("Sink err %s\n", err)
|
|||
// return
|
|||
// }
|
|||
// }
|
|||
//
|
|||
// wg := sync.WaitGroup{}
|
|||
// wg.Add(maxCnt)
|
|||
//
|
|||
// // 多个协程消费同一个channel
|
|||
// for i := 0; i < maxCnt; i++ {
|
|||
// go func() {
|
|||
// defer wg.Done()
|
|||
//
|
|||
// for data := range in {
|
|||
// syncProcess(data)
|
|||
// }
|
|||
// }()
|
|||
// }
|
|||
//
|
|||
// wg.Wait()
|
|||
//
|
|||
// return nil
|
|||
//}
|
@ -0,0 +1,17 @@ |
|||
package processors |
|||
|
|||
import ( |
|||
"context" |
|||
) |
|||
|
|||
type IProcessor interface { |
|||
Process(ctx context.Context, params any) (any, error) |
|||
} |
|||
|
|||
type ISource interface { |
|||
Process(ctx context.Context) (<-chan any, error) |
|||
} |
|||
|
|||
type ISink interface { |
|||
Process(ctx context.Context, params any) error |
|||
} |
@ -0,0 +1,58 @@ |
|||
package processors |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"gitea.anxinyun.cn/container/common_utils/dbHelper" |
|||
"log" |
|||
) |
|||
|
|||
func NewEsSink() ISink { |
|||
return &EsSink{ |
|||
esESHelper: dbHelper.NewESHelper([]string{"http://10.8.30.160:30092"}, "", ""), |
|||
} |
|||
} |
|||
|
|||
type EsSink struct { |
|||
esESHelper *dbHelper.ESHelper |
|||
} |
|||
|
|||
func (s *EsSink) Process(ctx context.Context, params any) error { |
|||
|
|||
log.Printf("es sink 操作") |
|||
|
|||
switch params.(type) { |
|||
case *common_models.DeviceData: |
|||
esRawList := s.toEsRaw([]*common_models.DeviceData{params.(*common_models.DeviceData)}) |
|||
s.dumpRaw(esRawList) |
|||
case *common_models.ProcessData: |
|||
esRawList := s.toEsRaw([]*common_models.DeviceData{¶ms.(*common_models.ProcessData).DeviceData}) |
|||
s.dumpRaw(esRawList) |
|||
default: |
|||
return errors.New("不支持的类型数据") |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (s *EsSink) toEsRaw(deviceDataList []*common_models.DeviceData) []common_models.EsRaw { |
|||
var createNativeRaws []common_models.EsRaw |
|||
for _, deviceData := range deviceDataList { |
|||
dataOutMeta := deviceData.DeviceInfo.DeviceMeta.GetOutputProps() |
|||
createNativeRaws = append(createNativeRaws, common_models.EsRaw{ |
|||
StructId: deviceData.StructId, |
|||
IotaDeviceName: deviceData.Name, |
|||
Data: deviceData.Raw, |
|||
CollectTime: deviceData.AcqTime, |
|||
Meta: dataOutMeta, |
|||
IotaDevice: deviceData.DeviceId, |
|||
CreateTime: deviceData.RealTime, |
|||
}) |
|||
} |
|||
|
|||
return createNativeRaws |
|||
} |
|||
func (s *EsSink) dumpRaw(esRaws []common_models.EsRaw) { |
|||
esIndex := "go_native_raws" |
|||
go s.esESHelper.BulkWriteRaws2Es(esIndex, esRaws) |
|||
} |
@ -0,0 +1,27 @@ |
|||
package processors |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
) |
|||
|
|||
type GroupProcess struct { |
|||
name string |
|||
} |
|||
|
|||
func NewGroupProcess(name string) *Process { |
|||
return &Process{name: name} |
|||
} |
|||
|
|||
// Process 分组处理者 , ProcessData -> PyhData
|
|||
func (p *GroupProcess) Process(ctx context.Context, params any) (any, error) { |
|||
log.Printf("[%s]开始执行", p.name) |
|||
if ProcessData, ok := params.(*common_models.ProcessData); !ok { |
|||
return nil, errors.New("不是[PyhData]类型数据") |
|||
} else { |
|||
log.Printf("[%s]-[%s]处理...", ProcessData.DeviceData.DeviceId, ProcessData.DeviceData.Name) |
|||
return ProcessData, nil |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
package processors |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
) |
|||
|
|||
type Process struct { |
|||
name string |
|||
} |
|||
|
|||
func NewProcess(name string) *Process { |
|||
return &Process{name: name} |
|||
} |
|||
|
|||
func (p *Process) Process(ctx context.Context, params any) (any, error) { |
|||
log.Printf("[%s]开始执行", p.name) |
|||
if ProcessData, ok := params.(*common_models.ProcessData); !ok { |
|||
return nil, errors.New("不是[DeviceData]类型数据") |
|||
} else { |
|||
log.Printf("[%s]-[%s]处理...", ProcessData.DeviceData.DeviceId, ProcessData.DeviceData.Name) |
|||
return ProcessData, nil |
|||
} |
|||
} |
@ -0,0 +1,76 @@ |
|||
module node |
|||
|
|||
go 1.22.0 |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 |
|||
github.com/google/uuid v1.6.0 |
|||
) |
|||
|
|||
require ( |
|||
gitea.anxinyun.cn/container/common_calc v0.0.1 // indirect |
|||
github.com/IBM/sarama v1.43.0 // indirect |
|||
github.com/allegro/bigcache v1.2.1 // indirect |
|||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect |
|||
github.com/beorn7/perks v1.0.1 // indirect |
|||
github.com/cespare/xxhash/v2 v2.2.0 // indirect |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
|||
github.com/eapache/go-resiliency v1.6.0 // indirect |
|||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect |
|||
github.com/eapache/queue v1.1.0 // indirect |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect |
|||
github.com/eko/gocache/lib/v4 v4.1.5 // indirect |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 // indirect |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 // indirect |
|||
github.com/elastic/go-elasticsearch/v6 v6.8.10 // indirect |
|||
github.com/fsnotify/fsnotify v1.7.0 // indirect |
|||
github.com/golang/mock v1.6.0 // indirect |
|||
github.com/golang/protobuf v1.5.3 // indirect |
|||
github.com/golang/snappy v0.0.4 // indirect |
|||
github.com/gorilla/websocket v1.5.0 // indirect |
|||
github.com/hashicorp/errwrap v1.0.0 // indirect |
|||
github.com/hashicorp/go-multierror v1.1.1 // indirect |
|||
github.com/hashicorp/go-uuid v1.0.3 // indirect |
|||
github.com/hashicorp/hcl v1.0.0 // indirect |
|||
github.com/influxdata/influxdb-client-go/v2 v2.13.0 // indirect |
|||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect |
|||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect |
|||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect |
|||
github.com/jcmturner/gofork v1.7.6 // indirect |
|||
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect |
|||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect |
|||
github.com/klauspost/compress v1.17.7 // indirect |
|||
github.com/magiconair/properties v1.8.7 // indirect |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect |
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect |
|||
github.com/oapi-codegen/runtime v1.0.0 // indirect |
|||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect |
|||
github.com/pierrec/lz4/v4 v4.1.21 // indirect |
|||
github.com/prometheus/client_golang v1.14.0 // indirect |
|||
github.com/prometheus/client_model v0.3.0 // indirect |
|||
github.com/prometheus/common v0.37.0 // indirect |
|||
github.com/prometheus/procfs v0.8.0 // indirect |
|||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect |
|||
github.com/redis/go-redis/v9 v9.5.1 // indirect |
|||
github.com/sagikazarmark/locafero v0.4.0 // indirect |
|||
github.com/sagikazarmark/slog-shim v0.1.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/spf13/viper v1.18.2 // indirect |
|||
github.com/subosito/gotenv v1.6.0 // indirect |
|||
go.uber.org/atomic v1.9.0 // indirect |
|||
go.uber.org/multierr v1.9.0 // indirect |
|||
golang.org/x/crypto v0.19.0 // indirect |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect |
|||
golang.org/x/net v0.21.0 // indirect |
|||
golang.org/x/sync v0.6.0 // indirect |
|||
golang.org/x/sys v0.17.0 // indirect |
|||
golang.org/x/text v0.14.0 // indirect |
|||
google.golang.org/protobuf v1.31.0 // indirect |
|||
gopkg.in/ini.v1 v1.67.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,654 @@ |
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= |
|||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= |
|||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= |
|||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= |
|||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= |
|||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= |
|||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= |
|||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= |
|||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= |
|||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= |
|||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= |
|||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= |
|||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= |
|||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= |
|||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= |
|||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= |
|||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= |
|||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= |
|||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= |
|||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= |
|||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= |
|||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= |
|||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= |
|||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= |
|||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= |
|||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= |
|||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= |
|||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= |
|||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= |
|||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= |
|||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= |
|||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= |
|||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= |
|||
gitea.anxinyun.cn/container/common_calc v0.0.1 h1:qJpZrhwJ99BENrsnH5Pa0i/x+78KmsHH06McK7Ky31s= |
|||
gitea.anxinyun.cn/container/common_calc v0.0.1/go.mod h1:fz4gOSd4XU918xaICOI7KtkoChfcyfvxURNXthARnR0= |
|||
gitea.anxinyun.cn/container/common_models v0.0.7 h1:tnk0LS3UZqyZwc3e3X9oRtkutMr5VkDbkEFgeQiVBTo= |
|||
gitea.anxinyun.cn/container/common_models v0.0.7/go.mod h1:i+0toGVrtTNHf5lxLQp573UOljDqbiG2iCY8729L8fk= |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7 h1:3j/qkZEv7mdDJJ+4hHy6A78cQLHenuJmv9NtPmr4H/8= |
|||
gitea.anxinyun.cn/container/common_utils v0.0.7/go.mod h1:rnWO6guEoy+fLovZJRzmhFwyq3/zwiXyzV2qa3U3L58= |
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= |
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= |
|||
github.com/IBM/sarama v1.43.0 h1:YFFDn8mMI2QL0wOrG0J2sFoVIAFl7hS9JQi2YZsXtJc= |
|||
github.com/IBM/sarama v1.43.0/go.mod h1:zlE6HEbC/SMQ9mhEYaF7nNLYOUyrs0obySKCckWP9BM= |
|||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= |
|||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= |
|||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= |
|||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= |
|||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= |
|||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= |
|||
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= |
|||
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= |
|||
github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= |
|||
github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= |
|||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= |
|||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= |
|||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= |
|||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= |
|||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= |
|||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= |
|||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= |
|||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= |
|||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= |
|||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= |
|||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= |
|||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= |
|||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= |
|||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= |
|||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= |
|||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= |
|||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= |
|||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= |
|||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= |
|||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= |
|||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= |
|||
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= |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= |
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= |
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= |
|||
github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30= |
|||
github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= |
|||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= |
|||
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= |
|||
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= |
|||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= |
|||
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= |
|||
github.com/eko/gocache/lib/v4 v4.1.5 h1:CeMQmdIzwBKKLRjk3FCDXzNFsQTyqJ01JLI7Ib0C9r8= |
|||
github.com/eko/gocache/lib/v4 v4.1.5/go.mod h1:XaNfCwW8KYW1bRZ/KoHA1TugnnkMz0/gT51NDIu7LSY= |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1 h1:xf9R5HZqmrfT4+NzlJPQJQUWftfWW06FHbjz4IEjE08= |
|||
github.com/eko/gocache/store/bigcache/v4 v4.2.1/go.mod h1:Q9+hxUE+XUVGSRGP1tqW8sPHcZ50PfyBVh9VKh0OjrA= |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1 h1:uPAgZIn7knH6a55tO4ETN9V93VD3Rcyx0ZIyozEqC0I= |
|||
github.com/eko/gocache/store/redis/v4 v4.2.1/go.mod h1:JoLkNA5yeGNQUwINAM9529cDNQCo88WwiKlO9e/+39I= |
|||
github.com/elastic/go-elasticsearch/v6 v6.8.10 h1:2lN0gJ93gMBXvkhwih5xquldszpm8FlUwqG5sPzr6a8= |
|||
github.com/elastic/go-elasticsearch/v6 v6.8.10/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI= |
|||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= |
|||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= |
|||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= |
|||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= |
|||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= |
|||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= |
|||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= |
|||
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/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= |
|||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= |
|||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= |
|||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= |
|||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= |
|||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= |
|||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= |
|||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= |
|||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= |
|||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= |
|||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= |
|||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= |
|||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= |
|||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= |
|||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= |
|||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= |
|||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= |
|||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= |
|||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= |
|||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= |
|||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= |
|||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= |
|||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= |
|||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= |
|||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= |
|||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= |
|||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
|||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
|||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
|||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= |
|||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= |
|||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= |
|||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= |
|||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= |
|||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= |
|||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= |
|||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= |
|||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= |
|||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= |
|||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= |
|||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= |
|||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= |
|||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= |
|||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= |
|||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= |
|||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= |
|||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= |
|||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= |
|||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= |
|||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
|||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
|||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
|||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
|||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
|||
github.com/google/go-cmp v0.5.4/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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= |
|||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= |
|||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= |
|||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= |
|||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= |
|||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= |
|||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= |
|||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= |
|||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= |
|||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= |
|||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= |
|||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= |
|||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= |
|||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= |
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
|||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= |
|||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= |
|||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= |
|||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= |
|||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= |
|||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= |
|||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= |
|||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= |
|||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= |
|||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= |
|||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= |
|||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= |
|||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= |
|||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= |
|||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= |
|||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= |
|||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= |
|||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= |
|||
github.com/influxdata/influxdb-client-go/v2 v2.13.0 h1:ioBbLmR5NMbAjP4UVA5r9b5xGjpABD7j65pI8kFphDM= |
|||
github.com/influxdata/influxdb-client-go/v2 v2.13.0/go.mod h1:k+spCbt9hcvqvUiz0sr5D8LolXHqAAOfPw9v/RIRHl4= |
|||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= |
|||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= |
|||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= |
|||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= |
|||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= |
|||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= |
|||
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= |
|||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= |
|||
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= |
|||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= |
|||
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= |
|||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= |
|||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= |
|||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= |
|||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= |
|||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= |
|||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= |
|||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= |
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= |
|||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= |
|||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= |
|||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= |
|||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= |
|||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= |
|||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= |
|||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= |
|||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= |
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
|||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= |
|||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= |
|||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= |
|||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= |
|||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= |
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= |
|||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= |
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= |
|||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= |
|||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= |
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= |
|||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= |
|||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= |
|||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |
|||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= |
|||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= |
|||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= |
|||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= |
|||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= |
|||
github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= |
|||
github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= |
|||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= |
|||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= |
|||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= |
|||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= |
|||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
|||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
|||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= |
|||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
|||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= |
|||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= |
|||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= |
|||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= |
|||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= |
|||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= |
|||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= |
|||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= |
|||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= |
|||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= |
|||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= |
|||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= |
|||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= |
|||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= |
|||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= |
|||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= |
|||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= |
|||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= |
|||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= |
|||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= |
|||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= |
|||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= |
|||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= |
|||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= |
|||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= |
|||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= |
|||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= |
|||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= |
|||
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= |
|||
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= |
|||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= |
|||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= |
|||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= |
|||
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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= |
|||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= |
|||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= |
|||
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= |
|||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= |
|||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= |
|||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= |
|||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= |
|||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= |
|||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= |
|||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= |
|||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= |
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
|||
github.com/stretchr/objx v0.1.1/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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
|||
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.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= |
|||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= |
|||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= |
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= |
|||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= |
|||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= |
|||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= |
|||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= |
|||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= |
|||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= |
|||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= |
|||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= |
|||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= |
|||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= |
|||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= |
|||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= |
|||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
|||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
|||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
|||
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.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= |
|||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= |
|||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= |
|||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= |
|||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= |
|||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= |
|||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= |
|||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= |
|||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= |
|||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= |
|||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= |
|||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= |
|||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= |
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= |
|||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= |
|||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= |
|||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= |
|||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= |
|||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= |
|||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
|||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
|||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
|||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
|||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= |
|||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= |
|||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= |
|||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= |
|||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= |
|||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= |
|||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= |
|||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= |
|||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= |
|||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
|||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
|||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
|||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= |
|||
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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
|||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
|||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
|||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
|||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
|||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
|||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
|||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= |
|||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
|||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= |
|||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= |
|||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= |
|||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= |
|||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= |
|||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= |
|||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= |
|||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= |
|||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= |
|||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= |
|||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= |
|||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= |
|||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= |
|||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= |
|||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= |
|||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= |
|||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= |
|||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= |
|||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= |
|||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
|||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
|||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
|||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
|||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= |
|||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= |
|||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
|||
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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= |
|||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= |
|||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
|||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
|||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
|||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
|||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
|||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
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.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= |
|||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
|||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
|||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= |
|||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= |
|||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
|||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
|||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
|||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= |
|||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
|||
golang.org/x/text v0.3.6/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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= |
|||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= |
|||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= |
|||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= |
|||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= |
|||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
|||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
|||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= |
|||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
|||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
|||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
|||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= |
|||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= |
|||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= |
|||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
|||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
|||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
|||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
|||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
|||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
|||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
|||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
|||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
|||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
|||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
|||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= |
|||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= |
|||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= |
|||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= |
|||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= |
|||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= |
|||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= |
|||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= |
|||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= |
|||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= |
|||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= |
|||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= |
|||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
|||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
|||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
|||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= |
|||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= |
|||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= |
|||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= |
|||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= |
|||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= |
|||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= |
|||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= |
|||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= |
|||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= |
|||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= |
|||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= |
|||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= |
|||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= |
|||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= |
|||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= |
|||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= |
|||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= |
|||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= |
|||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= |
|||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= |
|||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= |
|||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= |
|||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= |
|||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= |
|||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= |
|||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= |
|||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= |
|||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= |
|||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= |
|||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
|||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
|||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
|||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
|||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
|||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
|||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= |
|||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
|||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
|||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
|||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
|||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
|||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
|||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
|||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
|||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= |
|||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= |
|||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= |
|||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= |
|||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= |
|||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= |
|||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= |
|||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= |
|||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= |
|||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= |
|||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= |
|||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= |
|||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= |
|||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= |
|||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= |
|||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= |
|||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= |
|||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= |
|||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= |
|||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= |
|||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= |
|||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= |
|||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= |
|||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= |
|||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= |
|||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= |
|||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= |
|||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= |
|||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= |
|||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= |
|||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= |
|||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= |
|||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= |
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
|||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= |
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= |
|||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= |
|||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= |
|||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= |
|||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
|||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
|||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
|||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
|||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
|||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= |
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
|||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= |
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
|||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
|||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
|||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
|||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
|||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= |
|||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= |
|||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= |
|||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= |
|||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= |
|||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= |
@ -0,0 +1,9 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"node/app" |
|||
) |
|||
|
|||
func main() { |
|||
app.Start() |
|||
} |
@ -0,0 +1,63 @@ |
|||
package stages |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
"time" |
|||
) |
|||
|
|||
// 阶段处理
|
|||
type Stage struct { |
|||
Name string |
|||
In <-chan *common_models.ProcessData |
|||
processFuncs []func(*common_models.ProcessData) *common_models.ProcessData |
|||
Out chan *common_models.ProcessData |
|||
} |
|||
|
|||
func NewStage(name string) *Stage { |
|||
return &Stage{ |
|||
Name: name, |
|||
processFuncs: make([]func(*common_models.ProcessData) *common_models.ProcessData, 0), |
|||
In: make(<-chan *common_models.ProcessData, 2), |
|||
Out: make(chan *common_models.ProcessData, 2), |
|||
} |
|||
} |
|||
|
|||
func (s *Stage) StageRun() <-chan *common_models.ProcessData { |
|||
go func() { |
|||
defer func() { |
|||
close(s.Out) |
|||
log.Printf("[%s]关闭out", s.Name) |
|||
}() |
|||
for n := range s.In { |
|||
//log.Printf("[%s]处理数据 %v", s.Name, n.DeviceData.Name)
|
|||
s.Out <- s.process(n) |
|||
} |
|||
log.Printf("%s over", s.Name) |
|||
}() |
|||
|
|||
return s.Out |
|||
} |
|||
|
|||
// AddProcess 添加处理者。处理函数定义 func(*ProcessData) *ProcessData
|
|||
func (s *Stage) AddProcess(fun func(*common_models.ProcessData) *common_models.ProcessData) { |
|||
s.processFuncs = append(s.processFuncs, fun) |
|||
} |
|||
|
|||
func (s *Stage) process(data *common_models.ProcessData) *common_models.ProcessData { |
|||
for _, processFunc := range s.processFuncs { |
|||
//tag := fmt.Sprintf("%d/%d", i+1, len(s.processFuncs))
|
|||
//log.Printf("stage[%s][%s]流程处理 start=> %s", s.Name, tag, data.DeviceData.Name)
|
|||
func() { |
|||
defer timeCost(s.Name, data.DeviceData.DeviceId, time.Now()) |
|||
data = processFunc(data) |
|||
}() |
|||
//log.Printf("stage[%s][%s]流程处理 over=> %s", s.Name, tag, data.DeviceData.Name)
|
|||
} |
|||
|
|||
return data |
|||
} |
|||
func timeCost(nodeId, deviceId string, start time.Time) { |
|||
tc := time.Since(start) |
|||
log.Printf("stage[%s] ->[%s]设备数据耗时 = %v", nodeId, deviceId, tc) |
|||
} |
@ -0,0 +1,52 @@ |
|||
package stages |
|||
|
|||
import ( |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
) |
|||
|
|||
type StageManager struct { |
|||
in <-chan *common_models.ProcessData |
|||
Stages []Stage |
|||
out <-chan *common_models.ProcessData |
|||
} |
|||
|
|||
func NewStageManager() *StageManager { |
|||
return &StageManager{} |
|||
} |
|||
|
|||
func (the *StageManager) Run() { |
|||
if len(the.Stages) == 0 { |
|||
log.Panicf("Stages.len=%d 无有效处理流程", len(the.Stages)) |
|||
} |
|||
for i := 0; i < len(the.Stages); i++ { |
|||
if i == 0 { |
|||
the.Stages[i].In = the.in |
|||
} else { |
|||
the.Stages[i].In = the.Stages[i-1].Out |
|||
} |
|||
the.Stages[i].StageRun() |
|||
|
|||
} |
|||
the.out = the.Stages[len(the.Stages)-1].Out |
|||
|
|||
//todo 替换为sink
|
|||
go func() { |
|||
for { |
|||
a := <-the.out |
|||
log.Printf("流程处理完毕===>%s", a.DeviceData.Name) |
|||
} |
|||
}() |
|||
} |
|||
|
|||
func (the *StageManager) AddSource(source <-chan *common_models.ProcessData) { |
|||
the.in = source |
|||
} |
|||
|
|||
func (the *StageManager) AddOut(source chan *common_models.ProcessData) { |
|||
the.out = source |
|||
} |
|||
|
|||
func (the *StageManager) AddStages(stages ...Stage) { |
|||
the.Stages = append(the.Stages, stages...) |
|||
} |
@ -0,0 +1,140 @@ |
|||
package stages |
|||
|
|||
import ( |
|||
"fmt" |
|||
"gitea.anxinyun.cn/container/common_models" |
|||
"log" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestStageProcess(t *testing.T) { |
|||
log.SetFlags(log.Lshortfile | log.Lmicroseconds) |
|||
|
|||
stage1 := NewStage("打印DeviceId") |
|||
stage1.AddProcess(printDeviceId) |
|||
stage2 := NewStage("打印DeviceName") |
|||
stage2.AddProcess(printDeviceName) |
|||
sm := NewStageManager() |
|||
sm.AddStages(*stage1, *stage2) |
|||
source := make(chan *common_models.ProcessData, 2) |
|||
sm.AddSource(source) |
|||
sm.Run() |
|||
i := 0 |
|||
go func() { |
|||
for { |
|||
i++ |
|||
time.Sleep(time.Second * 2) |
|||
deviceId := fmt.Sprintf("%d", i) |
|||
pd := &common_models.ProcessData{ |
|||
DeviceData: common_models.DeviceData{ |
|||
DeviceId: deviceId, |
|||
Name: "Name-" + deviceId, |
|||
ThingId: "", |
|||
StructId: 0, |
|||
TaskId: "", |
|||
AcqTime: time.Time{}, |
|||
RealTime: time.Time{}, |
|||
ErrCode: 0, |
|||
Raw: nil, |
|||
DeviceInfo: common_models.DeviceInfo{}, |
|||
DimensionId: "", |
|||
}, |
|||
Stations: []common_models.Station{}, |
|||
} |
|||
source <- pd |
|||
log.Printf("进入数据 %d", i) |
|||
} |
|||
}() |
|||
for { |
|||
sinkData := <-sm.out |
|||
log.Printf("最终数据 %s", sinkData.DeviceData.Name) |
|||
} |
|||
|
|||
} |
|||
func printDeviceId(p *common_models.ProcessData) *common_models.ProcessData { |
|||
|
|||
log.Printf("流程1处理 %s", p.DeviceData.DeviceId) |
|||
return p |
|||
} |
|||
func printDeviceName(p *common_models.ProcessData) *common_models.ProcessData { |
|||
|
|||
log.Printf("流程2处理 %s", p.DeviceData.Name) |
|||
return p |
|||
} |
|||
|
|||
func TestStageRaw(t *testing.T) { |
|||
|
|||
//stage1 := NewStage("加")
|
|||
//stage1.AddProcess(Add1)
|
|||
//stage1.AddProcess(Add1000)
|
|||
//
|
|||
//stage2 := NewStage("减")
|
|||
//stage2.AddProcess(Sub1)
|
|||
//stage2.AddProcess(Sub1000)
|
|||
//
|
|||
//in := make(chan any, 3)
|
|||
//out := stage2.StageRun(stage1.StageRun(in))
|
|||
//
|
|||
//time.Sleep(time.Second * 1)
|
|||
//go func() {
|
|||
// for {
|
|||
// time.Sleep(time.Second * 2)
|
|||
// log.Printf("处理过的数据 %v [len=%v]", <-out, len(out))
|
|||
// }
|
|||
//}()
|
|||
//
|
|||
//var i = 0
|
|||
//for {
|
|||
// i += 1
|
|||
// in <- i
|
|||
//
|
|||
// log.Printf("====> 进入数据 %d", i)
|
|||
// time.Sleep(time.Second * 2)
|
|||
//
|
|||
// if i > 5 {
|
|||
// break
|
|||
// }
|
|||
//}
|
|||
} |
|||
|
|||
func Add[T int | float64](a T) T { |
|||
|
|||
return a + 1 |
|||
} |
|||
|
|||
func Add1(a any) any { |
|||
|
|||
if v, ok := a.(int); ok { |
|||
v += 1 |
|||
return v |
|||
} |
|||
return a |
|||
} |
|||
|
|||
func Add1000(a any) any { |
|||
|
|||
if v, ok := a.(int); ok { |
|||
v += 1000 |
|||
return v |
|||
} |
|||
return a |
|||
} |
|||
|
|||
func Sub1(a any) any { |
|||
|
|||
if v, ok := a.(int); ok { |
|||
v -= 1 |
|||
return v |
|||
} |
|||
return a |
|||
} |
|||
|
|||
func Sub1000(a any) any { |
|||
|
|||
if v, ok := a.(int); ok { |
|||
v -= 1000 |
|||
return v |
|||
} |
|||
return a |
|||
} |
@ -0,0 +1 @@ |
|||
ALTER TABLE "t_device_sensor" ADD COLUMN formula_id INTEGER; |