From d220e4bb7a8c7706928d0eb29b6f927521571892 Mon Sep 17 00:00:00 2001 From: lucas Date: Thu, 19 Sep 2024 10:39:06 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=BB=BA=20et-go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_files/microk8s部署.png | Bin 0 -> 51141 bytes README_files/node下线自动注销.png | Bin 0 -> 13829 bytes README_files/raw时序库.png | Bin 0 -> 83772 bytes .../t_device_sensor添加列 formula_id.png | Bin 0 -> 23795 bytes README_files/时序库bucket.png | Bin 0 -> 14332 bytes README_files/演示运行et-0.png | Bin 0 -> 25601 bytes README_files/演示运行et-1.png | Bin 0 -> 20952 bytes .../状态集副本数设置2及以上.png | Bin 0 -> 37626 bytes build/Dockerfile | 18 + build/Dockerfile_app | 7 + build/etgo.yaml | 122 + build/jenkinsfile_image | 26 + build/jenkinsfile_image_app | 40 + config.yaml | 56 + containerApp/go.mod | 28 + containerApp/main.go | 41 + dataSource/channels.go | 30 + dataSource/go.mod | 69 + dataSource/kafka/aggData.go | 36 + dataSource/kafka/iotaData.go | 22 + dataSource/kafka/kafka_handler.go | 69 + et_Info/InfoHandler.go | 78 + et_Info/go.mod | 51 + et_Info/info_test.go | 38 + et_analyze/aggThreshold.go | 145 + et_analyze/aggThreshold_test.go | 53 + et_analyze/go.mod | 83 + et_analyze/threshold.go | 172 ++ et_analyze/threshold_test.go | 44 + et_cache/cacheHandler.go | 89 + et_cache/cacheSer/cacheServer.go | 90 + et_cache/filterFor.go | 106 + et_cache/go.mod | 53 + et_calc/algorithm/fft.go | 62 + et_calc/algorithm/fft_test.go | 1291 ++++++++ .../strainCompensationByTemperature.go | 42 + et_calc/algorithm/vibCalc.go | 24 + et_calc/dataCalc.go | 245 ++ et_calc/dataCalc_test.go | 41 + et_calc/go.mod | 57 + et_calc/group/calcTask.go | 233 ++ et_calc/group/calcTaskKey.go | 21 + et_calc/group/calcTask_test.go | 31 + et_calc/group/groupCalc.go | 155 + et_calc/group/groupDueTask.go | 1 + et_calc/group/models_test.go | 17 + et_calc/group/timeStrategy.go | 99 + et_calc/unitTrans.go | 22 + et_print/go.mod | 2 + et_print/printHandler.go | 31 + et_prometheus_exporter/api.go | 84 + et_prometheus_exporter/api_test.go | 21 + et_prometheus_exporter/go.mod | 3 + et_push/go.mod | 69 + et_push/pushHandler.go | 122 + et_sink/go.mod | 58 + et_sink/sinkHandler.go | 251 ++ go.work | 16 + master/app/app.go | 25 + master/app/et_master.go | 318 ++ master/go.mod | 31 + master/go.sum | 74 + master/main.go | 10 + master/main_test.go | 204 ++ node/agg_node_test.go | 53 + node/agg_worker/agg_node.go | 85 + node/app/app.go | 121 + node/app/et_node.go | 222 ++ node/et_worker/et_recv/recvDataHanler.go | 212 ++ node/et_worker/processorManager.go | 114 + node/et_worker/processors/IProcessor.go | 17 + node/et_worker/processors/esSink.go | 58 + node/et_worker/processors/groupProcess.go | 27 + node/et_worker/processors/process.go | 26 + node/go.mod | 76 + node/go.sum | 654 ++++ node/main.go | 9 + node/node_test.go | 2721 +++++++++++++++++ node/stages/stage.go | 63 + node/stages/stageManage.go | 52 + node/stages/stage_test.go | 140 + .../t_device_sensor添加列 formula_id.sql | 1 + 82 files changed, 9877 insertions(+) create mode 100644 README_files/microk8s部署.png create mode 100644 README_files/node下线自动注销.png create mode 100644 README_files/raw时序库.png create mode 100644 README_files/t_device_sensor添加列 formula_id.png create mode 100644 README_files/时序库bucket.png create mode 100644 README_files/演示运行et-0.png create mode 100644 README_files/演示运行et-1.png create mode 100644 README_files/状态集副本数设置2及以上.png create mode 100644 build/Dockerfile create mode 100644 build/Dockerfile_app create mode 100644 build/etgo.yaml create mode 100644 build/jenkinsfile_image create mode 100644 build/jenkinsfile_image_app create mode 100644 config.yaml create mode 100644 containerApp/go.mod create mode 100644 containerApp/main.go create mode 100644 dataSource/channels.go create mode 100644 dataSource/go.mod create mode 100644 dataSource/kafka/aggData.go create mode 100644 dataSource/kafka/iotaData.go create mode 100644 dataSource/kafka/kafka_handler.go create mode 100644 et_Info/InfoHandler.go create mode 100644 et_Info/go.mod create mode 100644 et_Info/info_test.go create mode 100644 et_analyze/aggThreshold.go create mode 100644 et_analyze/aggThreshold_test.go create mode 100644 et_analyze/go.mod create mode 100644 et_analyze/threshold.go create mode 100644 et_analyze/threshold_test.go create mode 100644 et_cache/cacheHandler.go create mode 100644 et_cache/cacheSer/cacheServer.go create mode 100644 et_cache/filterFor.go create mode 100644 et_cache/go.mod create mode 100644 et_calc/algorithm/fft.go create mode 100644 et_calc/algorithm/fft_test.go create mode 100644 et_calc/algorithm/strainCompensationByTemperature.go create mode 100644 et_calc/algorithm/vibCalc.go create mode 100644 et_calc/dataCalc.go create mode 100644 et_calc/dataCalc_test.go create mode 100644 et_calc/go.mod create mode 100644 et_calc/group/calcTask.go create mode 100644 et_calc/group/calcTaskKey.go create mode 100644 et_calc/group/calcTask_test.go create mode 100644 et_calc/group/groupCalc.go create mode 100644 et_calc/group/groupDueTask.go create mode 100644 et_calc/group/models_test.go create mode 100644 et_calc/group/timeStrategy.go create mode 100644 et_calc/unitTrans.go create mode 100644 et_print/go.mod create mode 100644 et_print/printHandler.go create mode 100644 et_prometheus_exporter/api.go create mode 100644 et_prometheus_exporter/api_test.go create mode 100644 et_prometheus_exporter/go.mod create mode 100644 et_push/go.mod create mode 100644 et_push/pushHandler.go create mode 100644 et_sink/go.mod create mode 100644 et_sink/sinkHandler.go create mode 100644 go.work create mode 100644 master/app/app.go create mode 100644 master/app/et_master.go create mode 100644 master/go.mod create mode 100644 master/go.sum create mode 100644 master/main.go create mode 100644 master/main_test.go create mode 100644 node/agg_node_test.go create mode 100644 node/agg_worker/agg_node.go create mode 100644 node/app/app.go create mode 100644 node/app/et_node.go create mode 100644 node/et_worker/et_recv/recvDataHanler.go create mode 100644 node/et_worker/processorManager.go create mode 100644 node/et_worker/processors/IProcessor.go create mode 100644 node/et_worker/processors/esSink.go create mode 100644 node/et_worker/processors/groupProcess.go create mode 100644 node/et_worker/processors/process.go create mode 100644 node/go.mod create mode 100644 node/go.sum create mode 100644 node/main.go create mode 100644 node/node_test.go create mode 100644 node/stages/stage.go create mode 100644 node/stages/stageManage.go create mode 100644 node/stages/stage_test.go create mode 100644 script/t_device_sensor添加列 formula_id.sql diff --git a/README_files/microk8s部署.png b/README_files/microk8s部署.png new file mode 100644 index 0000000000000000000000000000000000000000..b66d8d23b5a9b2220f37874fabb81098033fc40e GIT binary patch literal 51141 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%0D*R|)vc|a>k!w5>&U9Sx^uFZb%_d4o}r7sc|Zkd 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 literal 0 HcmV?d00001 diff --git a/README_files/node下线自动注销.png b/README_files/node下线自动注销.png new file mode 100644 index 0000000000000000000000000000000000000000..6ea4b34715af194ffe6982630d7324c607deae9e GIT binary patch literal 13829 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 B`m6u| literal 0 HcmV?d00001 diff --git a/README_files/raw时序库.png b/README_files/raw时序库.png new file mode 100644 index 0000000000000000000000000000000000000000..0f79da4d74110716b28ff7c673b34d1a23cb1c58 GIT binary patch literal 83772 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

w`Ok&ouLT-wi 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 NyDcxBaZCT@{{xAaYYhMZ literal 0 HcmV?d00001 diff --git a/README_files/t_device_sensor添加列 formula_id.png b/README_files/t_device_sensor添加列 formula_id.png new file mode 100644 index 0000000000000000000000000000000000000000..4a8cc25e8ec543fdf80080b20cd09450dbcbfd71 GIT binary patch literal 23795 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 KT`JbN74}~{(H33+ literal 0 HcmV?d00001 diff --git a/README_files/时序库bucket.png b/README_files/时序库bucket.png new file mode 100644 index 0000000000000000000000000000000000000000..0badd48858961af2a689b1d2b4f04d2e2705997a GIT binary patch literal 14332 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 literal 0 HcmV?d00001 diff --git a/README_files/演示运行et-0.png b/README_files/演示运行et-0.png new file mode 100644 index 0000000000000000000000000000000000000000..6574ee6391a222f6dea35c4c44ca56e3cae0b183 GIT binary patch literal 25601 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 QStFx&?0K*D-S@xvKf|}P>i_@% literal 0 HcmV?d00001 diff --git a/README_files/状态集副本数设置2及以上.png b/README_files/状态集副本数设置2及以上.png new file mode 100644 index 0000000000000000000000000000000000000000..b50c79a32e5ef10db7d0f8612b832a7101551678 GIT binary patch literal 37626 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 bg)iSlUUrdoxMjMp0go%z6LastspS6x&e8|0 literal 0 HcmV?d00001 diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 0000000..339447a --- /dev/null +++ b/build/Dockerfile @@ -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"] \ No newline at end of file diff --git a/build/Dockerfile_app b/build/Dockerfile_app new file mode 100644 index 0000000..18bb176 --- /dev/null +++ b/build/Dockerfile_app @@ -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"] diff --git a/build/etgo.yaml b/build/etgo.yaml new file mode 100644 index 0000000..3bb911f --- /dev/null +++ b/build/etgo.yaml @@ -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 diff --git a/build/jenkinsfile_image b/build/jenkinsfile_image new file mode 100644 index 0000000..28609a5 --- /dev/null +++ b/build/jenkinsfile_image @@ -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}" + } + } +} \ No newline at end of file diff --git a/build/jenkinsfile_image_app b/build/jenkinsfile_image_app new file mode 100644 index 0000000..d66c032 --- /dev/null +++ b/build/jenkinsfile_image_app @@ -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}" + } + } +} \ No newline at end of file diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..0f55c57 --- /dev/null +++ b/config.yaml @@ -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 \ No newline at end of file diff --git a/containerApp/go.mod b/containerApp/go.mod new file mode 100644 index 0000000..9076376 --- /dev/null +++ b/containerApp/go.mod @@ -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 +) diff --git a/containerApp/main.go b/containerApp/main.go new file mode 100644 index 0000000..f09eb98 --- /dev/null +++ b/containerApp/main.go @@ -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 + }() +} diff --git a/dataSource/channels.go b/dataSource/channels.go new file mode 100644 index 0000000..b6b8b7d --- /dev/null +++ b/dataSource/channels.go @@ -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 +} diff --git a/dataSource/go.mod b/dataSource/go.mod new file mode 100644 index 0000000..6f1e119 --- /dev/null +++ b/dataSource/go.mod @@ -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 +) diff --git a/dataSource/kafka/aggData.go b/dataSource/kafka/aggData.go new file mode 100644 index 0000000..8e7f0cf --- /dev/null +++ b/dataSource/kafka/aggData.go @@ -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 +} diff --git a/dataSource/kafka/iotaData.go b/dataSource/kafka/iotaData.go new file mode 100644 index 0000000..a1e0b1f --- /dev/null +++ b/dataSource/kafka/iotaData.go @@ -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 +} diff --git a/dataSource/kafka/kafka_handler.go b/dataSource/kafka/kafka_handler.go new file mode 100644 index 0000000..281b298 --- /dev/null +++ b/dataSource/kafka/kafka_handler.go @@ -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 + } +} diff --git a/et_Info/InfoHandler.go b/et_Info/InfoHandler.go new file mode 100644 index 0000000..bd310a7 --- /dev/null +++ b/et_Info/InfoHandler.go @@ -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) + } +} diff --git a/et_Info/go.mod b/et_Info/go.mod new file mode 100644 index 0000000..e7bc296 --- /dev/null +++ b/et_Info/go.mod @@ -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 +) diff --git a/et_Info/info_test.go b/et_Info/info_test.go new file mode 100644 index 0000000..24dde7f --- /dev/null +++ b/et_Info/info_test.go @@ -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("=====") +} diff --git a/et_analyze/aggThreshold.go b/et_analyze/aggThreshold.go new file mode 100644 index 0000000..1f6e619 --- /dev/null +++ b/et_analyze/aggThreshold.go @@ -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 +} diff --git a/et_analyze/aggThreshold_test.go b/et_analyze/aggThreshold_test.go new file mode 100644 index 0000000..7f4d419 --- /dev/null +++ b/et_analyze/aggThreshold_test.go @@ -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 处理 +} diff --git a/et_analyze/go.mod b/et_analyze/go.mod new file mode 100644 index 0000000..a10cd25 --- /dev/null +++ b/et_analyze/go.mod @@ -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 +) diff --git a/et_analyze/threshold.go b/et_analyze/threshold.go new file mode 100644 index 0000000..5457456 --- /dev/null +++ b/et_analyze/threshold.go @@ -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 +} diff --git a/et_analyze/threshold_test.go b/et_analyze/threshold_test.go new file mode 100644 index 0000000..2cca69f --- /dev/null +++ b/et_analyze/threshold_test.go @@ -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 传递到下个环节 +} diff --git a/et_cache/cacheHandler.go b/et_cache/cacheHandler.go new file mode 100644 index 0000000..b23d458 --- /dev/null +++ b/et_cache/cacheHandler.go @@ -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 +} diff --git a/et_cache/cacheSer/cacheServer.go b/et_cache/cacheSer/cacheServer.go new file mode 100644 index 0000000..9657c25 --- /dev/null +++ b/et_cache/cacheSer/cacheServer.go @@ -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 +} diff --git a/et_cache/filterFor.go b/et_cache/filterFor.go new file mode 100644 index 0000000..5c4fda7 --- /dev/null +++ b/et_cache/filterFor.go @@ -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 +} diff --git a/et_cache/go.mod b/et_cache/go.mod new file mode 100644 index 0000000..00b9fbe --- /dev/null +++ b/et_cache/go.mod @@ -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 +) diff --git a/et_calc/algorithm/fft.go b/et_calc/algorithm/fft.go new file mode 100644 index 0000000..2d97b45 --- /dev/null +++ b/et_calc/algorithm/fft.go @@ -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 +} diff --git a/et_calc/algorithm/fft_test.go b/et_calc/algorithm/fft_test.go new file mode 100644 index 0000000..15aaab2 --- /dev/null +++ b/et_calc/algorithm/fft_test.go @@ -0,0 +1,1291 @@ +package algorithm + +var org = []float64{-3.369140625, + -3.1083984375, + -2.5849609375, + -3.6482421875, + -2.7185546875, + -2.7927734375, + -2.727734375, + -2.8470703125, + -2.6486328125, + -2.5947265625, + -3.1, + -3.1947265625, + -3.873046875, + -2.3876953125, + -2.94765625, + -2.8171875, + -1.9599609375, + -2.5716796875, + -2.6443359375, + -3.315625, + -3.294140625, + -2.123828125, + -3.3798828125, + -3.663671875, + -2.653515625, + -2.4787109375, + -2.4060546875, + -2.4173828125, + -2.5384765625, + -2.8482421875, + -3.7009765625, + -2.8197265625, + -2.882421875, + -3.5326171875, + -2.6271484375, + -2.6255859375, + -2.637109375, + -2.5099609375, + -2.9541015625, + -1.6419921875, + -3.342578125, + -3.6662109375, + -2.8396484375, + -3.3861328125, + -2.6544921875, + -2.7728515625, + -2.4783203125, + -2.044921875, + -1.981640625, + -2.5615234375, + -3.1599609375, + -3.44375, + -3.2537109375, + -3.408984375, + -3.607421875, + -2.403515625, + -2.3091796875, + -2.3953125, + -2.758203125, + -3.2435546875, + -2.6515625, + -3.017578125, + -3.1416015625, + -3.859375, + -3.6478515625, + -2.6216796875, + -2.7560546875, + -2.8595703125, + -2.6013671875, + -1.5955078125, + -3.05390625, + -3.742578125, + -3.028125, + -3.06171875, + -2.8798828125, + -3.4880859375, + -2.60390625, + -2.0025390625, + -2.25546875, + -2.5513671875, + -3.237109375, + -3.2962890625, + -3.6244140625, + -3.43125, + -3.7521484375, + -2.73359375, + -2.769140625, + -2.175390625, + -2.2771484375, + -3.3037109375, + -2.770703125, + -3.6560546875, + -3.8791015625, + -4.1845703125, + -3.3998046875, + -2.7298828125, + -2.571875, + -2.519921875, + -2.4009765625, + -2.4728515625, + -2.8322265625, + -2.7595703125, + -3.3296875, + -3.1740234375, + -3.5421875, + -2.8625, + -2.5791015625, + -2.2263671875, + -1.3212890625, + -2.6013671875, + -3.18515625, + -3.2447265625, + -4.401171875, + -2.775390625, + -3.827734375, + -3.866796875, + -2.6572265625, + -2.7341796875, + -2.2505859375, + -3.4703125, + -3.134375, + -3.1072265625, + -3.45703125, + -3.7310546875, + -3.2107421875, + -2.52890625, + -2.2056640625, + -2.8220703125, + -2.724609375, + -2.3373046875, + -3.2892578125, + -2.9224609375, + -3.6955078125, + -3.9080078125, + -2.6794921875, + -3.09140625, + -2.3005859375, + -2.740625, + -3.11953125, + -2.1771484375, + -4.023828125, + -3.6087890625, + -3.2771484375, + -3.1396484375, + -3.4462890625, + -2.9853515625, + -2.6986328125, + -2.8357421875, + -2.5470703125, + -3.186328125, + -3.76171875, + -3.9330078125, + -3.38828125, + -3.4791015625, + -2.91484375, + -3.017578125, + -3.079296875, + -2.2689453125, + -2.972265625, + -2.88828125, + -3.00859375, + -2.58203125, + -3.1470703125, + -2.9775390625, + -3.34140625, + -3.3791015625, + -2.9017578125, + -2.76328125, + -2.75546875, + -2.8931640625, + -2.7806640625, + -3.1271484375, + -3.5404296875, + -3.188671875, + -2.6154296875, + -2.8345703125, + -3.144140625, + -2.238671875, + -2.5740234375, + -3.853125, + -2.7919921875, + -2.585546875, + -3.3091796875, + -3.33046875, + -3.625390625, + -2.6416015625, + -2.369140625, + -3.11328125, + -3.355859375, + -2.6345703125, + -1.9828125, + -3.166796875, + -3.2888671875, + -2.8240234375, + -2.61953125, + -3.6525390625, + -3.53359375, + -1.9546875, + -2.7939453125, + -2.72578125, + -3.2880859375, + -3.1771484375, + -2.894921875, + -3.54453125, + -3.1322265625, + -3.5658203125, + -3.269140625, + -2.406640625, + -2.775, + -2.3169921875, + -2.07421875, + -2.392578125, + -2.48515625, + -3.9435546875, + -3.65390625, + -2.455859375, + -3.301171875, + -3.3994140625, + -2.2421875, + -2.4259765625, + -2.78359375, + -2.8384765625, + -2.506640625, + -3.2322265625, + -3.129296875, + -3.562109375, + -2.8826171875, + -2.29140625, + -3.1310546875, + -2.8072265625, + -2.4861328125, + -3.083984375, + -2.9853515625, + -3.137890625, + -3.49140625, + -2.685546875, + -3.18515625, + -3.2447265625, + -4.401171875, + -2.775390625, + -3.827734375, + -3.866796875, + -2.6572265625, + -2.7341796875, + -2.2505859375, + -3.4703125, + -3.134375, + -3.1072265625, + -3.45703125, + -3.7310546875, + -3.2107421875, + -2.52890625, + -2.2056640625, + -2.8220703125, + -2.724609375, + -2.3373046875, + -3.2892578125, + -2.9224609375, + -3.6955078125, + -3.9080078125, + -2.6794921875, + -3.09140625, + -2.3005859375, + -2.740625, + -3.11953125, + -2.1771484375, + -4.023828125, + -3.6087890625, + -3.2771484375, + -3.1396484375, + -3.4462890625, + -2.9853515625, + -2.6986328125, + -2.8357421875, + -2.5470703125, + -3.186328125, + -3.76171875, + -3.9330078125, + -3.38828125, + -3.4791015625, + -2.91484375, + -3.017578125, + -3.079296875, + -2.2689453125, + -3.69921875, + -2.837890625, + -1.76796875, + -2.3697265625, + -2.3154296875, + -2.7044921875, + -3.030859375, + -3.2119140625, + -3.6689453125, + -2.8154296875, + -3.0322265625, + -3.324609375, + -2.2650390625, + -2.3037109375, + -2.09921875, + -3.109765625, + -3.1083984375, + -3.3708984375, + -4.312890625, + -4.0884765625, + -2.78828125, + -2.931640625, + -3.0865234375, + -1.8298828125, + -2.4896484375, + -2.9650390625, + -2.9443359375, + -3.7458984375, + -3.0345703125, + -4.0197265625, + -3.076171875, + -2.3462890625, + -2.6720703125, + -2.0224609375, + -2.120703125, + -2.8322265625, + -2.707421875, + -3.63046875, + -3.9998046875, + -2.65078125, + -3.980078125, + -3.328125, + -1.99765625, + -2.609765625, + -2.44765625, + -3.1078125, + -3.3376953125, + -3.2814453125, + -3.6169921875, + -3.753515625, + -3.311328125, + -3.0849609375, + -2.8806640625, + -2.4552734375, + -3.190625, + -2.2109375, + -2.8822265625, + -3.6671875, + -3.44296875, + -3.96640625, + -3.009375, + -2.97578125, + -2.8416015625, + -3.0580078125, + -2.1294921875, + -1.6970703125, + -3.0548828125, + -2.7083984375, + -3.979296875, + -3.60703125, + -2.977734375, + -3.7716796875, + -2.3640625, + -2.8162109375, + -2.095703125, + -1.731640625, + -3.2521484375, + -2.4486328125, + -2.954296875, + -3.698828125, + -3.5439453125, + -3.7240234375, + -2.8462890625, + -2.29140625, + -3.446484375, + -1.9263671875, + -1.9591796875, + -3.3396484375, + -3.1693359375, + -4.43515625, + -3.26953125, + -2.6203125, + -3.8634765625, + -2.6357421875, + -2.2189453125, + -2.186328125, + -2.5439453125, + -3.9888671875, + -3.2703125, + -3.1419921875, + -3.93828125, + -3.5462890625, + -2.582421875, + -2.6091796875, + -1.6158203125, + -2.3939453125, + -2.9681640625, + -2.65703125, + -3.6150390625, + -3.9166015625, + -3.83046875, + -3.5689453125, + -2.4935546875, + -1.6876953125, + -2.8416015625, + -2.2349609375, + -2.5080078125, + -3.8396484375, + -3.6361328125, + -3.6904296875, + -3.63984375, + -3.429296875, + -2.378125, + -2.6525390625, + -2.09921875, + -2.2455078125, + -2.723046875, + -3.0705078125, + -3.51171875, + -3.400390625, + -4.4275390625, + -3.0619140625, + -2.5921875, + -3.0591796875, + -2.2482421875, + -2.132421875, + -2.8869140625, + -3.539453125, + -3.1431640625, + -3.6466796875, + -3.907421875, + -3.811328125, + -2.791015625, + -2.445703125, + -2.9265625, + -2.3255859375, + -1.796875, + -3.355078125, + -3.849609375, + -3.772265625, + -3.08515625, + -3.59296875, + -3.9625, + -1.8833984375, + -1.55, + -2.0837890625, + -2.690625, + -3.3919921875, + -3.4693359375, + -2.4517578125, + -3.4927734375, + -4.666015625, + -3.10625, + -2.6224609375, + -1.2630859375, + -3.209765625, + -3.38359375, + -2.339453125, + -3.432421875, + -4.2921875, + -3.4123046875, + -3.1810546875, + -3.2328125, + -2.6130859375, + -2.8919921875, + -1.3939453125, + -3.3685546875, + -3.0369140625, + -2.8462890625, + -4.2005859375, + -3.77734375, + -3.41796875, + -3.12578125, + -1.7365234375, + -2.2220703125, + -3.51796875, + -2.73203125, + -2.5033203125, + -2.590234375, + -4.1345703125, + -4.885546875, + -2.462890625, + -2.301171875, + -3.74453125, + -2.7314453125, + -2.2267578125, + -2.032421875, + -2.99765625, + -4.876953125, + -2.8837890625, + -2.221875, + -3.175, + -3.0125, + -3.70390625, + -1.3765625, + -1.8458984375, + -4.2771484375, + -3.7908203125, + -2.591015625, + -3.798046875, + -3.2640625, + -4.0669921875, + -1.778125, + -2.148046875, + -4.0611328125, + -1.91796875, + -2.5044921875, + -2.9107421875, + -3.4517578125, + -4.3951171875, + -3.0673828125, + -1.556640625, + -2.971484375, + -2.751953125, + -4.1796875, + -2.3396484375, + -1.7591796875, + -4.619921875, + -3.9498046875, + -2.944140625, + -3.453125, + -2.40703125, + -2.7703125, + -3.3005859375, + -2.128515625, + -3.3076171875, + -2.7423828125, + -3.6623046875, + -3.7609375, + -2.4224609375, + -2.8880859375, + -2.9302734375, + -3.002734375, + -3.30625, + -2.480078125, + -2.291015625, + -3.262109375, + -3.8041015625, + -2.939453125, + -3.837890625, + -2.3896484375, + -2.9533203125, + -3.1291015625, + -2.132421875, + -3.6587890625, + -2.220703125, + -2.599609375, + -3.8802734375, + -3.572265625, + -2.6447265625, + -1.93203125, + -3.980078125, + -4.201171875, + -1.79921875, + -2.3353515625, + -3.299609375, + -3.808203125, + -2.5560546875, + -2.81484375, + -2.942578125, + -3.343359375, + -3.1482421875, + -2.5298828125, + -3.12109375, + -3.412109375, + -3.99921875, + -2.3017578125, + -3.1650390625, + -3.3091796875, + -3.1408203125, + -2.364453125, + -3.31328125, + -3.82421875, + -3.20390625, + -2.541796875, + -2.73203125, + -4.6048828125, + -2.5982421875, + -2.6982421875, + -2.0041015625, + -4.0369140625, + -3.68984375, + -3.2236328125, + -2.1884765625, + -2.20703125, + -5.0947265625, + -2.7630859375, + -1.428515625, + -2.7279296875, + -3.3296875, + -2.930859375, + -3.1408203125, + -3.0220703125, + -3.7837890625, + -2.3359375, + -3.5359375, + -4.32734375, + -1.9193359375, + -1.3685546875, + -2.494140625, + -3.7072265625, + -2.8392578125, + -2.5150390625, + -2.621484375, + -4.0177734375, + -2.981640625, + -3.00078125, + -2.0302734375, + -2.489453125, + -4.389453125, + -2.76640625, + -1.891015625, + -3.4927734375, + -4.2595703125, + -3.489453125, + -2.1564453125, + -3.136328125, + -3.0125, + -1.5115234375, + -2.736328125, + -3.3087890625, + -3.28046875, + -1.8822265625, + -2.0595703125, + -3.5396484375, + -3.7048828125, + -2.4541015625, + -3.0443359375, + -2.6814453125, + -3.266015625, + -4.369140625, + -1.9896484375, + -2.2306640625, + -3.8927734375, + -3.4076171875, + -2.6494140625, + -1.434765625, + -2.019921875, + -3.797265625, + -2.4412109375, + -2.8626953125, + -2.7185546875, + -2.4458984375, + -4.415234375, + -3.8689453125, + -1.6845703125, + -0.6748046875, + -4.1890625, + -4.1953125, + -2.8296875, + -1.9953125, + -2.5419921875, + -4.9, + -3.8404296875, + -2.5751953125, + -1.90625, + -3.201171875, + -3.5365234375, + -2.694140625, + -1.1373046875, + -2.202734375, + -4.712109375, + -3.5501953125, + -1.3236328125, + -2.958984375, + -4.08359375, + -3.0779296875, + -1.476953125, + -3.42734375, + -3.594921875, + -3.2853515625, + -2.82265625, + -2.2921875, + -3.9767578125, + -3.5658203125, + -2.2935546875, + -2.40234375, + -3.4373046875, + -4.13359375, + -2.4904296875, + -0.819921875, + -3.559375, + -3.794921875, + -2.1431640625, + -3.46875, + -3.3265625, + -3.2876953125, + -1.8267578125, + -2.7751953125, + -3.607421875, + -2.39375, + -2.7833984375, + -3.3984375, + -3.444140625, + -3.6033203125, + -2.383203125, + -2.558203125, + -3.4576171875, + -2.537109375, + -0.78125, + -1.735546875, + -3.7939453125, + -4.6029296875, + -2.134765625, + -1.684765625, + -5.0484375, + -3.7625, + -1.466015625, + -1.5876953125, + -3.7201171875, + -3.7046875, + -1.8203125, + -2.848828125, + -3.80234375, + -4.259375, + -2.8703125, + -2.021875, + -2.4814453125, + -2.8298828125, + -2.869140625, + -2.027734375, + -1.74375, + -3.9115234375, + -4.625, + -2.285546875, + -3.1708984375, + -3.9548828125, + -2.737109375, + -2.281640625, + -1.9490234375, + -3.634375, + -2.8619140625, + -2.0146484375, + -2.9685546875, + -3.5384765625, + -3.4546875, + -2.5765625, + -3.3443359375, + -3.025, + -2.6701171875, + -2.994921875, + -2.6521484375, + -3.243359375, + -3.3544921875, + -2.9892578125, + -2.4435546875, + -3.3353515625, + -2.29921875, + -2.5921875, + -3.6869140625, + -3.631640625, + -3.11796875, + -1.1978515625, + -4.097265625, + -4.367578125, + -1.040625, + -1.2869140625, + -2.4474609375, + -4.2990234375, + -1.6888671875, + -1.7263671875, + -4.32265625, + -3.090625, + -2.5083984375, + -3.27890625, + -2.975390625, + -3.0177734375, + -3.68828125, + -2.4771484375, + -2.273046875, + -4.0896484375, + -3.1548828125, + -3.045703125, + -2.2861328125, + -3.8734375, + -3.39375, + -1.1267578125, + -3.1666015625, + -2.887890625, + -2.769140625, + -1.641015625, + -2.7435546875, + -4.0279296875, + -3.7517578125, + -2.5533203125, + -2.5953125, + -2.3009765625, + -3.2828125, + -4.308984375, + -1.15625, + -2.2240234375, + -3.510546875, + -2.75546875, + -2.8578125, + -2.99296875, + -3.776953125, + -1.22421875, + -2.4759765625, + -4.5583984375, + -1.59296875, + -1.74765625, + -3.364453125, + -4.801953125, + -2.9564453125, + -1.7640625, + -4.1201171875, + -4.1861328125, + -1.3451171875, + -0.8470703125, + -3.3609375, + -2.635546875, + -3.803125, + -2.5935546875, + -2.968359375, + -4.1046875, + -2.734765625, + -2.48671875, + -1.4103515625, + -2.9771484375, + -2.180859375, + -1.542578125, + -2.8576171875, + -4.2001953125, + -3.201953125, + -2.2197265625, + -4.052734375, + -3.480859375, + -3.693359375, + -0.936328125, + -1.26171875, + -2.5603515625, + -2.761328125, + -3.4462890625, + -1.70625, + -3.4775390625, + -5.283203125, + -3.3919921875, + -0.8943359375, + -1.10625, + -3.523828125, + -2.727734375, + -1.172265625, + -2.789453125, + -5.4427734375, + -3.4826171875, + -3.064453125, + -2.9943359375, + -2.6388671875, + -2.9818359375, + -1.440234375, + -2.3171875, + -2.2041015625, + -3.2267578125, + -3.7279296875, + -3.2033203125, + -3.449609375, + -3.4208984375, + -2.2125, + -1.6599609375, + -3.3267578125, + -2.32265625, + -2.0904296875, + -3.63359375, + -3.310546875, + -4.958984375, + -2.4876953125, + -2.599609375, + -3.27890625, + -1.63671875, + -2.956640625, + -2.0357421875, + -1.91328125, + -2.755078125, + -4.0244140625, + -3.9400390625, + -2.3484375, + -2.7162109375, + -3.1408203125, + -2.6318359375, + -1.86796875, + -2.614453125, + -3.29921875, + -3.4451171875, + -3.968359375, + -3.4998046875, + -2.406640625, + -2.5154296875, + -4.0560546875, + -2.281640625, + -1.3984375, + -2.7599609375, + -3.75625, + -3.2091796875, + -2.2572265625, + -3.9076171875, + -3.3080078125, + -3.0791015625, + -3.2921875, + -1.460546875, + -2.6904296875, + -3.2931640625, + -1.99375, + -2.0970703125, + -3.641015625, + -3.988671875, + -2.6228515625, + -1.77421875, + -3.6728515625, + -3.751171875, + -1.071484375, + -2.94296875, + -3.5908203125, + -2.8212890625, + -2.1318359375, + -2.166796875, + -4.0712890625, + -3.0357421875, + -3.4236328125, + -2.8158203125, + -1.6935546875, + -3.6916015625, + -4.617578125, + -1.8935546875, + -1.166015625, + -4.2013671875, + -3.599609375, + -2.484765625, + -1.7642578125, + -2.6345703125, + -3.7841796875, + -2.0306640625, + -3.109765625, + -1.9837890625, + -3.09609375, + -5.18359375, + -2.5818359375, + -2.134375, + -2.8640625, + -4.0796875, + -2.914453125, + -1.398046875, + -3.010546875, + -3.53125, + -3.301953125, + -2.76171875, + -3.585546875, + -4.0966796875, + -2.8650390625, + -2.1607421875, + -2.545703125, + -2.051171875, + -3.0326171875, + -3.3861328125, + -2.05859375, + -3.8765625, + -4.6810546875, + -3.1626953125, + -2.0224609375, + -2.613671875, + -3.9865234375, + -2.2388671875, + -2.09296875, + -3.3076171875, + -4.2212890625, + -3.7259765625, + -2.396875, + -2.953515625, + -2.6388671875, + -3.078515625, + -3.168359375, + -1.2921875, + -2.6013671875, + -4.805859375, + -3.1771484375, + -2.078125, + -3.877734375, + -3.5724609375, + -2.3240234375, + -1.8521484375, + -2.87578125, + -3.7142578125, + -1.804296875, + -3.510546875, + -4.201171875, + -3.364453125, + -3.0140625, + -3.1736328125, + -2.537109375, + -1.376953125, + -2.875, + -2.2296875, + -3.5017578125, + -3.9380859375, + -3.8818359375, + -3.44609375, + -1.651171875, + -3.5794921875, + -3.1671875, + -2.2318359375, + -2.501171875, + -2.4025390625, + -2.8970703125, + -3.2681640625, + -4.1314453125, + -2.6845703125, + -2.434375, + -3.0994140625, + -3.6640625, + -2.0025390625, + -2.212890625, + -2.791015625, + -2.5779296875, + -3.729296875, + -3.5529296875, + -3.1947265625, + -2.8958984375, + -3.758984375, + -3.8361328125, + -1.21328125, + -1.631640625, + -4.076171875, + -2.8921875, + -1.435546875, + -2.96484375, + -3.4865234375, + -3.6162109375, + -2.4068359375, + -3.075390625, + -3.7658203125, + -1.1134765625, + -2.9712890625, + -3.410546875, + -1.7427734375, + -3.3271484375, + -3.39140625, + -4.0947265625, + -2.780859375, + -3.062890625, + -3.3771484375, + -2.0390625, + -2.77421875, + -3.10390625, + -2.9025390625, + -2.57109375, + -3.7078125, + -2.848046875, + -2.679296875, + -3.1669921875, + -1.8837890625, + -2.9185546875, + -2.931640625, + -2.570703125, + -2.615234375, + -2.438671875, + -4.1244140625, + -3.6974609375, + -1.9224609375, + -3.5048828125, + -2.9267578125, + -2.31875, + -3.782421875, + -3.3787109375, + -1.987890625, + -2.4896484375, + -4.3421875, + -3.3478515625, + -1.225390625, + -3.1279296875, + -3.77578125, + -2.8048828125, + -2.047265625, + -2.7251953125, + -3.896484375, + -2.479296875, + -2.505078125, + -3.7712890625, + -2.3443359375, + -2.8970703125, + -3.330078125, + -3.0283203125, + -2.5765625, + -2.3767578125, + -2.9931640625, + -2.68359375, + -2.38984375, + -3.801171875, + -3.084765625, + -1.996875, + -3.38125, + -2.9984375, + -2.6755859375, + -2.3111328125, + -2.39609375, + -2.8515625, + -3.02265625, + -3.441015625, + -2.625390625, + -2.9357421875, + -4.3228515625, + -2.991796875, + -2.361328125, + -1.99609375, + -4.21171875, + -3.3544921875, + -2.1095703125, + -3.7095703125, + -3.286328125, + -2.92421875, + -3.593359375, + -3.334375, + -1.7185546875, + -2.3408203125, + -3.808984375, + -3.740234375, + -1.9580078125, + -2.724609375, + -4.908984375, + -2.93125, + -1.5203125, + -2.6466796875, + -2.8396484375, + -2.5099609375, + -3.0533203125, + -3.308203125, + -3.5490234375, + -3.09921875, + -4.0400390625, + -3.0390625, + -1.19140625, + -3.11015625, + -2.6669921875, + -2.38046875, + -4.2150390625, + -3.462890625, + -2.9759765625, + -2.3751953125, + -3.2837890625, + -3.984375, + -1.82578125, + -1.2232421875, + -2.74375, + -3.30703125, + -3.2296875, + -3.51015625, + -3.02109375, + -3.521484375, + -3.3541015625, + -2.0849609375, + -1.4677734375, + -2.5482421875, + -3.511328125, + -3.2291015625, + -1.5607421875, + -3.9650390625, + -3.7435546875, + -2.26953125, + -3.3755859375, + -2.7998046875, + -2.53671875, + -2.04140625, + -3.0087890625, + -3.491796875, + -2.7724609375, + -2.48671875, + -3.851171875, + -4.47890625, + -3.315234375, + -2.5380859375, + -1.805078125, + -2.7435546875, + -4.3720703125, + -2.3291015625, + -2.633203125, + -3.70234375, + -4.3091796875, + -3.2630859375, + -1.8087890625, + -3.081640625, + -3.1974609375, + -1.2849609375, + -1.8970703125, + -3.7486328125, + -3.557421875, + -2.247265625, + -3.091015625, + -3.3552734375, + -3.375, + -2.8130859375, + -1.3208984375, + -3.0341796875, + -3.6181640625, + -2.9919921875, + -3.0943359375, + -2.860546875, + -4.030078125, + -3.689453125, + -1.9626953125, + -2.6234375, + -2.4044921875, + -2.6662109375, + -2.090625, + -2.3640625, + -4.2015625, + -3.355859375, + -3.40625, + -3.1154296875, + -1.9986328125, + -2.88125, + -2.5943359375, + -2.883203125, + -2.267578125, + -3.3080078125, + -3.421875, + -3.37578125, + -2.8220703125, + -3.8181640625, + -2.067578125, + -1.737890625, + -4.3927734375, + -2.0984375, + -2.459765625, + -3.31640625, + -4.4091796875, + -3.05546875, + -2.4865234375, + -3.0125, + -3.314453125, + -2.1908203125, + -2.296484375, + -2.53828125, + -2.7958984375, + -3.0451171875, + -3.7283203125, + -4.0345703125, + -2.1712890625, + -3.3587890625, + -1.64765625, + -2.962109375, + -3.03515625, + -1.9431640625, + -2.4927734375, + -3.6318359375, + -3.7080078125, + -3.437109375, + -1.7865234375, + -2.176953125, + -3.936328125, + -1.760546875, + -2.43671875, + -2.1015625, + -2.71015625, + -3.472265625, + -3.58125, + -2.6658203125, + -2.798828125, + -2.6287109375, + -2.8150390625, + -2.5416015625, + -1.4705078125, + -3.3693359375, + -4.1064453125, + -4.0166015625, + -2.854296875, + -2.6861328125, + -2.8869140625, + -2.7484375, + -3.0123046875, + -2.33984375, + -1.35859375, + -3.7580078125, + -3.858984375, + -2.9111328125, + -2.9640625, + -2.9947265625, + -3.4783203125, + -1.3658203125, + -2.37578125, + -2.7427734375, + -3.4568359375, + -3.0380859375, + -3.5755859375, + -4.0435546875, + -3.4818359375, + -3.7365234375, + -2.24765625, + -2.716796875} +var d50 = []float64{112.89428987785185, 112.89163186198229, 112.92774385653463, 112.90665232832848, 112.90345626899293, 112.89050556698785, 112.9223388929967, 112.90684819647407, 112.91968761139091, 112.92015737263193, 112.95650803086707, 112.93183440263013, 112.92145533196171, 112.89805747615247, 112.91755501747937, 112.89066541587363, 112.89408157440488, 112.88668364927142, 112.9181982287467, 112.89350191830809, 112.8869811801277, 112.86869148075199, 112.8961172155654, 112.88473477828417, 112.90228116537921, 112.88696455372505, 112.90950442802188, 112.93234014518288, 112.87243088677049, 112.88318538658908, 112.91414539530385, 112.86323394545087, 112.84642971475027, 112.93513567998079, 112.8858490468651, 112.90107059053442, 112.8922100826528, 112.87954130275561, 112.91166900716479, 112.89648672625223, 112.90963823277383, 112.91043281067232, 112.9471145541186, 112.9227842697885, 112.91275315083998, 112.88970612729656, 112.90955193395142, 112.88301837346427, 112.88680586395493, 112.87981634740925} +var d64 = []float64{112.89428987785185, 112.89163186198229, 112.92774385653463, 112.90665232832848, 112.90345626899293, 112.89050556698785, 112.9223388929967, 112.90684819647407, 112.91968761139091, 112.92015737263193, 112.95650803086707, 112.93183440263013, 112.92145533196171, 112.89805747615247, 112.91755501747937, 112.89066541587363, 112.89408157440488, 112.88668364927142, 112.9181982287467, 112.89350191830809, 112.8869811801277, 112.86869148075199, 112.8961172155654, 112.88473477828417, 112.90228116537921, 112.88696455372505, 112.90950442802188, 112.93234014518288, 112.87243088677049, 112.88318538658908, 112.91414539530385, 112.86323394545087, 112.84642971475027, 112.93513567998079, 112.8858490468651, 112.90107059053442, 112.8922100826528, 112.87954130275561, 112.91166900716479, 112.89648672625223, 112.90963823277383, 112.91043281067232, 112.9471145541186, 112.9227842697885, 112.91275315083998, 112.88970612729656, 112.90955193395142, 112.88301837346427, 112.88680586395493, 112.87981634740925, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + +// TODO TestFFT_only() 报错 +//func TestFFT_only(t *testing.T) { +// rpf, fftArray := FFT_forward(d50, 1) +// println(rpf, fftArray) +// +//} diff --git a/et_calc/algorithm/strainCompensationByTemperature.go b/et_calc/algorithm/strainCompensationByTemperature.go new file mode 100644 index 0000000..d377e6c --- /dev/null +++ b/et_calc/algorithm/strainCompensationByTemperature.go @@ -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 +} diff --git a/et_calc/algorithm/vibCalc.go b/et_calc/algorithm/vibCalc.go new file mode 100644 index 0000000..372f1cd --- /dev/null +++ b/et_calc/algorithm/vibCalc.go @@ -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 +} diff --git a/et_calc/dataCalc.go b/et_calc/dataCalc.go new file mode 100644 index 0000000..d0587a8 --- /dev/null +++ b/et_calc/dataCalc.go @@ -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 +} diff --git a/et_calc/dataCalc_test.go b/et_calc/dataCalc_test.go new file mode 100644 index 0000000..7fc4c2a --- /dev/null +++ b/et_calc/dataCalc_test.go @@ -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("=====") +} diff --git a/et_calc/go.mod b/et_calc/go.mod new file mode 100644 index 0000000..382028d --- /dev/null +++ b/et_calc/go.mod @@ -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 +) diff --git a/et_calc/group/calcTask.go b/et_calc/group/calcTask.go new file mode 100644 index 0000000..513be54 --- /dev/null +++ b/et_calc/group/calcTask.go @@ -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 +} diff --git a/et_calc/group/calcTaskKey.go b/et_calc/group/calcTaskKey.go new file mode 100644 index 0000000..8acb968 --- /dev/null +++ b/et_calc/group/calcTaskKey.go @@ -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) +} diff --git a/et_calc/group/calcTask_test.go b/et_calc/group/calcTask_test.go new file mode 100644 index 0000000..39eb6a6 --- /dev/null +++ b/et_calc/group/calcTask_test.go @@ -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()) +} diff --git a/et_calc/group/groupCalc.go b/et_calc/group/groupCalc.go new file mode 100644 index 0000000..12d5b9b --- /dev/null +++ b/et_calc/group/groupCalc.go @@ -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() +// } +//} diff --git a/et_calc/group/groupDueTask.go b/et_calc/group/groupDueTask.go new file mode 100644 index 0000000..654521e --- /dev/null +++ b/et_calc/group/groupDueTask.go @@ -0,0 +1 @@ +package group diff --git a/et_calc/group/models_test.go b/et_calc/group/models_test.go new file mode 100644 index 0000000..83d7e8c --- /dev/null +++ b/et_calc/group/models_test.go @@ -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() +} diff --git a/et_calc/group/timeStrategy.go b/et_calc/group/timeStrategy.go new file mode 100644 index 0000000..d2e79a4 --- /dev/null +++ b/et_calc/group/timeStrategy.go @@ -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() +//} diff --git a/et_calc/unitTrans.go b/et_calc/unitTrans.go new file mode 100644 index 0000000..62a1817 --- /dev/null +++ b/et_calc/unitTrans.go @@ -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 +} diff --git a/et_print/go.mod b/et_print/go.mod new file mode 100644 index 0000000..0ba0217 --- /dev/null +++ b/et_print/go.mod @@ -0,0 +1,2 @@ +module et_print +go 1.22.0 \ No newline at end of file diff --git a/et_print/printHandler.go b/et_print/printHandler.go new file mode 100644 index 0000000..90fd1f4 --- /dev/null +++ b/et_print/printHandler.go @@ -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 +} diff --git a/et_prometheus_exporter/api.go b/et_prometheus_exporter/api.go new file mode 100644 index 0000000..0b7d816 --- /dev/null +++ b/et_prometheus_exporter/api.go @@ -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) +} diff --git a/et_prometheus_exporter/api_test.go b/et_prometheus_exporter/api_test.go new file mode 100644 index 0000000..6f2298b --- /dev/null +++ b/et_prometheus_exporter/api_test.go @@ -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() +} diff --git a/et_prometheus_exporter/go.mod b/et_prometheus_exporter/go.mod new file mode 100644 index 0000000..bed75ab --- /dev/null +++ b/et_prometheus_exporter/go.mod @@ -0,0 +1,3 @@ +module et_prometheus_exporter + +go 1.22 diff --git a/et_push/go.mod b/et_push/go.mod new file mode 100644 index 0000000..6ca7ec0 --- /dev/null +++ b/et_push/go.mod @@ -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 +) diff --git a/et_push/pushHandler.go b/et_push/pushHandler.go new file mode 100644 index 0000000..5ba8495 --- /dev/null +++ b/et_push/pushHandler.go @@ -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) + } + } +} diff --git a/et_sink/go.mod b/et_sink/go.mod new file mode 100644 index 0000000..4b8ead6 --- /dev/null +++ b/et_sink/go.mod @@ -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 +) diff --git a/et_sink/sinkHandler.go b/et_sink/sinkHandler.go new file mode 100644 index 0000000..5304685 --- /dev/null +++ b/et_sink/sinkHandler.go @@ -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) + } +} diff --git a/go.work b/go.work new file mode 100644 index 0000000..48271b2 --- /dev/null +++ b/go.work @@ -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 +) diff --git a/master/app/app.go b/master/app/app.go new file mode 100644 index 0000000..51bb52f --- /dev/null +++ b/master/app/app.go @@ -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) +} diff --git a/master/app/et_master.go b/master/app/et_master.go new file mode 100644 index 0000000..c2c8337 --- /dev/null +++ b/master/app/et_master.go @@ -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) +} diff --git a/master/go.mod b/master/go.mod new file mode 100644 index 0000000..88e7651 --- /dev/null +++ b/master/go.mod @@ -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 +) diff --git a/master/go.sum b/master/go.sum new file mode 100644 index 0000000..e62f1e6 --- /dev/null +++ b/master/go.sum @@ -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= diff --git a/master/main.go b/master/main.go new file mode 100644 index 0000000..e84dc92 --- /dev/null +++ b/master/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "master/app" +) + +func main() { + app.Start() + println("over=======") +} diff --git a/master/main_test.go b/master/main_test.go new file mode 100644 index 0000000..3889d09 --- /dev/null +++ b/master/main_test.go @@ -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 +} diff --git a/node/agg_node_test.go b/node/agg_node_test.go new file mode 100644 index 0000000..7d4dbbb --- /dev/null +++ b/node/agg_node_test.go @@ -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("测试结束") +} diff --git a/node/agg_worker/agg_node.go b/node/agg_worker/agg_node.go new file mode 100644 index 0000000..02e0604 --- /dev/null +++ b/node/agg_worker/agg_node.go @@ -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 + } +} diff --git a/node/app/app.go b/node/app/app.go new file mode 100644 index 0000000..179754d --- /dev/null +++ b/node/app/app.go @@ -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 +} diff --git a/node/app/et_node.go b/node/app/et_node.go new file mode 100644 index 0000000..f97a0d7 --- /dev/null +++ b/node/app/et_node.go @@ -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) +} diff --git a/node/et_worker/et_recv/recvDataHanler.go b/node/et_worker/et_recv/recvDataHanler.go new file mode 100644 index 0000000..b3a55b7 --- /dev/null +++ b/node/et_worker/et_recv/recvDataHanler.go @@ -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) + } + } + } +} diff --git a/node/et_worker/processorManager.go b/node/et_worker/processorManager.go new file mode 100644 index 0000000..028e698 --- /dev/null +++ b/node/et_worker/processorManager.go @@ -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 +//} diff --git a/node/et_worker/processors/IProcessor.go b/node/et_worker/processors/IProcessor.go new file mode 100644 index 0000000..99a2c03 --- /dev/null +++ b/node/et_worker/processors/IProcessor.go @@ -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 +} diff --git a/node/et_worker/processors/esSink.go b/node/et_worker/processors/esSink.go new file mode 100644 index 0000000..f961bae --- /dev/null +++ b/node/et_worker/processors/esSink.go @@ -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) +} diff --git a/node/et_worker/processors/groupProcess.go b/node/et_worker/processors/groupProcess.go new file mode 100644 index 0000000..7cc7351 --- /dev/null +++ b/node/et_worker/processors/groupProcess.go @@ -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 + } +} diff --git a/node/et_worker/processors/process.go b/node/et_worker/processors/process.go new file mode 100644 index 0000000..4ef6154 --- /dev/null +++ b/node/et_worker/processors/process.go @@ -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 + } +} diff --git a/node/go.mod b/node/go.mod new file mode 100644 index 0000000..23e5ca1 --- /dev/null +++ b/node/go.mod @@ -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 +) diff --git a/node/go.sum b/node/go.sum new file mode 100644 index 0000000..cef15f2 --- /dev/null +++ b/node/go.sum @@ -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= diff --git a/node/main.go b/node/main.go new file mode 100644 index 0000000..2570842 --- /dev/null +++ b/node/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "node/app" +) + +func main() { + app.Start() +} diff --git a/node/node_test.go b/node/node_test.go new file mode 100644 index 0000000..e7d83b0 --- /dev/null +++ b/node/node_test.go @@ -0,0 +1,2721 @@ +package main + +import ( + "encoding/json" + "et_Info" + "et_cache" + "et_calc" + "et_push" + "et_sink" + "gitea.anxinyun.cn/container/common_models" + "log" + "node/app" + "node/stages" + "testing" + "time" +) + +func TestNodeHandler_WSD(t *testing.T) { + rawDataMsg_WSD := `{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "5da9aa1b-05b7-4943-be57-dedb34f7a1bd", + "dimensionId": "76c75371-bb9a-4f71-a25d-58adf7938296", + "dimCapId": "f8e00449-7dad-4ca8-a4eb-0284784efa8f", + "capId": "d2add1b3-b21c-420b-82a4-e0c55ee3a019", + "deviceId": "ed0f1d94-49a9-415b-9336-f965a2b0a985", + "scheduleId": "8c83fb77-48f3-493b-9311-1b6b7b4fa7a7", + "taskId": "0e1c7d3d-257d-4763-a335-198aef0fc625", + "jobId": 2, + "jobRepeatId": 1, + "triggerTime": "2024-07-06T18:01:48.001+08:00", + "realTime": "2024-07-06T18:01:48.001+08:00", + "finishTime": "2024-07-06T18:01:48.001+08:00", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "humidy": 60.12, + "Temp": 30.2 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 1, + "dropped": false + } + } +}` + + rawData := &common_models.IotaData{} + err := json.Unmarshal([]byte(rawDataMsg_WSD), rawData) + if err != nil { + log.Panicf("测试异常%s", err.Error()) + } + + nodeWorker := app.NewEtWorker() + nodeStageManage := stages.NewStageManager() + nodeStageManage.AddSource(nodeWorker.LoadCh()) + sinkRawHandler := et_sink.NewSinkRawHandler() + nodeStageManage.AddStages(sinkRawHandler.GetStage()) + infoHandler := et_Info.NewInfoHandler() + nodeStageManage.AddStages(infoHandler.GetStage()) + calcHandler := et_calc.NewCalcHandler() + nodeStageManage.AddStages(calcHandler.GetStage()) + sinkThemeHandler := et_sink.NewSinkThemeHandler() + nodeStageManage.AddStages(sinkThemeHandler.GetStage()) + publishHandler := et_push.NewPushHandler() + nodeStageManage.AddStages(publishHandler.GetStage()) + + nodeStageManage.Run() + + log.Println("测试开始") + nodeWorker.ConsumerProcess(rawData) + time.Sleep(time.Second * 20) + log.Println("测试结束") +} +func TestNodeHandler_CZ(t *testing.T) { + + rawDataMsg_CZ := `{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "8e3eec71-c924-47fd-ac8b-2f28c49ad4e9", + "dimensionId": "a460675c-fb42-4f9e-80b9-d50b51597618", + "dimCapId": "59632dd8-d616-4520-baf6-63b7b7993b78", + "capId": "aacfd8a2-92a5-41f9-8884-b41a43c74280", + "deviceId": "1dd0f0d2-e802-487e-91fc-d0d3f24ddbfc", + "scheduleId": "83dfb3bf-0523-41b6-907f-e2a8f542050d", + "taskId": "ec9410fa-a9f1-4ba2-831a-aa52331558d6", + "jobId": 3, + "jobRepeatId": 1, + "triggerTime": "2024-07-08T11:09:32+08:00", + "realTime": "0001-01-01T00:00:00Z", + "finishTime": "2024-07-08T11:09:41.34+08:00", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "axieSpeed": "71", + "axieWeight": "[\"0.9\",\"0.4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"]", + "axisnum": "2", + "cmsLength": "2457", + "crossRoad": "1", + "direction": "2", + "grossWeight": 6.3, + "overload": "未超载", + "totalBase": "0", + "weightK": 1 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 0, + "dropped": false + } + } + }` + rawData := &common_models.IotaData{} + err := json.Unmarshal([]byte(rawDataMsg_CZ), rawData) + if err != nil { + log.Panicf("测试异常%s", err.Error()) + } + + nodeWorker := app.NewEtWorker() + nodeStageManage := stages.NewStageManager() + nodeStageManage.AddSource(nodeWorker.LoadCh()) + sinkRawHandler := et_sink.NewSinkRawHandler() + nodeStageManage.AddStages(sinkRawHandler.GetStage()) + infoHandler := et_Info.NewInfoHandler() + nodeStageManage.AddStages(infoHandler.GetStage()) + calcHandler := et_calc.NewCalcHandler() + nodeStageManage.AddStages(calcHandler.GetStage()) + sinkThemeHandler := et_sink.NewSinkThemeHandler() + nodeStageManage.AddStages(sinkThemeHandler.GetStage()) + publishHandler := et_push.NewPushHandler() + nodeStageManage.AddStages(publishHandler.GetStage()) + + nodeStageManage.Run() + + log.Println("测试开始") + nodeWorker.ConsumerProcess(rawData) + time.Sleep(time.Second * 20) + log.Println("测试结束") +} + +func TestNodeHandler_Formula303(t *testing.T) { + rawDataMsg_YB := `{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "8e3eec71-c924-47fd-ac8b-2f28c49ad4e9", + "dimensionId": "a460675c-fb42-4f9e-80b9-d50b51597618", + "dimCapId": "c59552d1-69f4-4026-b69b-37d2dde33c7e", + "capId": "f22498e2-cec8-482a-bc08-673e1e30298f", + "deviceId": "c5eb15d8-6a3e-4a0e-8cca-59bf1d06465b", + "scheduleId": "f0380c9e-24e3-43e1-8135-c1e8df9ff7c4", + "taskId": "ad6a62f1-0af7-4c53-80dd-c1d7833cdf38", + "jobId": 1, + "jobRepeatId": 1, + "triggerTime": "2024-01-16T09:40:02.921+08:00", + "realTime": "0001-01-01T00:00:00Z", + "finishTime": "2024-01-16T09:45:10.556600693+08:00", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "frequency": 32906, + "physicalvalue": -10, + "waterlevel": -10 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 0, + "dropped": false + } + } + }` + rawData := &common_models.IotaData{} + err := json.Unmarshal([]byte(rawDataMsg_YB), rawData) + if err != nil { + log.Panicf("测试异常%s", err.Error()) + } + + nodeWorker := app.NewEtWorker() + nodeStageManage := stages.NewStageManager() + nodeStageManage.AddSource(nodeWorker.LoadCh()) + sinkRawHandler := et_sink.NewSinkRawHandler() + nodeStageManage.AddStages(sinkRawHandler.GetStage()) + infoHandler := et_Info.NewInfoHandler() + nodeStageManage.AddStages(infoHandler.GetStage()) + calcHandler := et_calc.NewCalcHandler() + nodeStageManage.AddStages(calcHandler.GetStage()) + sinkThemeHandler := et_sink.NewSinkThemeHandler() + nodeStageManage.AddStages(sinkThemeHandler.GetStage()) + nodeStageManage.Run() + + log.Println("测试开始") + nodeWorker.ConsumerProcess(rawData) + time.Sleep(time.Second * 100) +} + +func TestNodeHandler_Formula_104(t *testing.T) { + rawDataMsg_YB := ` +{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "8e3eec71-c924-47fd-ac8b-2f28c49ad4e9", + "dimensionId": "f65e5990-540d-40ff-9056-af775e5e4c56", + "dimCapId": "aaab627b-5a58-454b-a83f-e236a63930bb", + "capId": "35ec137a-ea04-4c81-bbaf-23ddae650c0e", + "deviceId": "9c94a305-dd18-4809-8fc6-4191c699e726", + "scheduleId": "c5559fbd-eaf4-4fd9-b667-30cc40607ea9", + "taskId": "c05ccef4-017c-48d5-a2e5-c01fac16f797", + "jobId": 1, + "jobRepeatId": 1, + "triggerTime": "2024-03-28T00:53:27.244Z", + "realTime": "2024-03-28T00:53:27.244Z", + "finishTime": "2024-03-28T00:53:27.244Z", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "pressure": 17.186, + "temperature": 23.25 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 0, + "dropped": false + } + } +}` + rawData := &common_models.IotaData{} + err := json.Unmarshal([]byte(rawDataMsg_YB), rawData) + if err != nil { + log.Panicf("测试异常%s", err.Error()) + } + + nodeWorker := app.NewEtWorker() + nodeStageManage := stages.NewStageManager() + nodeStageManage.AddSource(nodeWorker.LoadCh()) + sinkRawHandler := et_sink.NewSinkRawHandler() + nodeStageManage.AddStages(sinkRawHandler.GetStage()) + infoHandler := et_Info.NewInfoHandler() + nodeStageManage.AddStages(infoHandler.GetStage()) + calcHandler := et_calc.NewCalcHandler() + nodeStageManage.AddStages(calcHandler.GetStage()) + sinkThemeHandler := et_sink.NewSinkThemeHandler() + nodeStageManage.AddStages(sinkThemeHandler.GetStage()) + + nodeStageManage.Run() + + log.Println("测试开始") + nodeWorker.ConsumerProcess(rawData) + time.Sleep(time.Second * 10) + log.Println("测试结束") +} +func TestNodeHandler_vib(t *testing.T) { + rawDataMsg_vib := ` +{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "8e3eec71-c924-47fd-ac8b-2f28c49ad4e9", + "dimensionId": "a460675c-fb42-4f9e-80b9-d50b51597618", + "dimCapId": "ef658aa7-32dc-43a5-8d6f-f73299bbae9b", + "capId": "f22498e2-cec8-482a-bc08-673e1e30298f", + "deviceId": "283ed38f-29d2-45ce-b133-8e71c38b0ee5", + "scheduleId": "476fae85-0b60-454f-a41b-ad49c356dc5c", + "taskId": "6f5acbd4-138a-43cc-96b7-b99335ef8964", + "jobId": 2, + "jobRepeatId": 1, + "triggerTime": "2024-07-10T15:00:59+08:00", + "realTime": "0001-01-01T00:00:00Z", + "finishTime": "2024-07-10T15:00:59+08:00", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "_data_type": "vib", + "filterFreq": 8, + "gainAmplifier": 1, + "physicalvalue": [ + 0.138672, + 0.11777359999999999, + 0.0951168, + 0.0277344, + -0.1117184, + 0.1152344, + -0.0349608, + -0.023828, + -0.1884768, + -0.1126952, + 0.18437520000000002, + 0, + -0.047656, + -0.0974608, + -0.1531248, + -0.1310544, + 0.1433592, + 0.070508, + -0.0810544, + -0.0332032, + -0.0757816, + -0.11406240000000001, + -0.08984400000000001, + -0.07832, + -0.05410160000000001, + 0.00918, + 0.0218752, + 0.1371096, + 0.0128904, + 0.05312480000000001, + 0.0937504, + 0.1 + ], + "sampleFreq": 16, + "triggerType": 4, + "version": 1 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 0, + "dropped": false + } + } + }` + rawData := &common_models.IotaData{} + err := json.Unmarshal([]byte(rawDataMsg_vib), rawData) + if err != nil { + log.Panicf("测试异常%s", err.Error()) + } + + nodeWorker := app.NewEtWorker() + nodeStageManage := stages.NewStageManager() + nodeStageManage.AddSource(nodeWorker.LoadCh()) + sinkRawHandler := et_sink.NewSinkRawHandler() + nodeStageManage.AddStages(sinkRawHandler.GetStage()) + infoHandler := et_Info.NewInfoHandler() + nodeStageManage.AddStages(infoHandler.GetStage()) + calcHandler := et_calc.NewCalcHandler() + nodeStageManage.AddStages(calcHandler.GetStage()) + sinkThemeHandler := et_sink.NewSinkThemeHandler() + nodeStageManage.AddStages(sinkThemeHandler.GetStage()) + + nodeStageManage.Run() + + log.Println("测试开始") + nodeWorker.ConsumerProcess(rawData) + time.Sleep(time.Second * 100) + log.Println("测试结束") +} + +// 对标安心云的振动数据 判断主题是否一致 +func TestNodeHandler_vib2(t *testing.T) { + rawDataMsg_vib := ` +{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "8e3eec71-c924-47fd-ac8b-2f28c49ad4e9", + "dimensionId": "a460675c-fb42-4f9e-80b9-d50b51597618", + "dimCapId": "ef658aa7-32dc-43a5-8d6f-f73299bbae9b", + "capId": "f22498e2-cec8-482a-bc08-673e1e30298f", + "deviceId": "283ed38f-29d2-45ce-b133-8e71c38b0ee5", + "scheduleId": "476fae85-0b60-454f-a41b-ad49c356dc5c", + "taskId": "6f5acbd4-138a-43cc-96b7-b99335ef8964", + "jobId": 2, + "jobRepeatId": 1, + "triggerTime": "2024-04-10T19:16:59+08:00", + "realTime": "0001-01-01T00:00:00Z", + "finishTime": "2024-04-10T19:23:29.886332576+08:00", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "_data_type": "vib", + "filterFreq": 128, + "gainAmplifier": 0, + "physicalvalue": [ + -7.83457, + -7.843946, + -7.860351, + -7.864355, + -7.862305, + -7.864844, + -7.859961, + -7.861817, + -7.87539, + -7.861914, + -7.860938, + -7.886133, + -7.89375, + -7.896191, + -7.890332, + -7.885938, + -7.879687, + -7.90791, + -7.925098, + -7.942578, + -7.951367, + -7.959668, + -7.932813, + -7.918457, + -7.902344, + -7.880664, + -7.838574, + -7.786426, + -7.782519, + -7.760547, + -7.766309, + -7.764062, + -7.742578, + -7.722558, + -7.72334, + -7.70625, + -7.680664, + -7.6625, + -7.649023, + -7.641406, + -7.662793, + -7.669434, + -7.692871, + -7.714746, + -7.731446, + -7.728711, + -7.743652, + -7.769629, + -7.789844, + -7.794238, + -7.812695, + -7.817676, + -7.826172, + -7.843359, + -7.866016, + -7.848145, + -7.85039, + -7.860351, + -7.864355, + -7.868457, + -7.889062, + -7.884668, + -7.888086, + -7.895019, + -7.785645, + -7.795898, + -7.806348, + -7.808203, + -7.816797, + -7.800977, + -7.808496, + -7.79795, + -7.752149, + -7.740918, + -7.732813, + -7.722754, + -7.72168, + -7.723926, + -7.719824, + -7.71377, + -7.684863, + -7.661426, + -7.665332, + -7.656446, + -7.641894, + -7.635547, + -7.644141, + -7.660547, + -7.720019, + -7.738086, + -7.786914, + -7.860938, + -7.882715, + -7.879981, + -7.883984, + -7.898047, + -7.900977, + -7.87832, + -7.837793, + -7.841894, + -7.823828, + -7.792676, + -7.804883, + -7.808106, + -7.819336, + -7.835254, + -7.826172, + -7.821875, + -7.823242, + -7.787988, + -7.762207, + -7.751562, + -7.726855, + -7.692676, + -7.670313, + -7.640039, + -7.63711, + -7.639453, + -7.667285, + -7.65039, + -7.654492, + -7.638672, + -7.638965, + -7.638379, + -7.638183, + -7.655566, + -6.717871, + -6.424805, + -6.234863, + -6.225977, + -6.332715, + -6.519824, + -6.869726, + -7.208886, + -7.446875, + -7.719238, + -7.799023, + -7.801758, + -7.813086, + -7.78457, + -7.781641, + -7.783106, + -7.8, + -7.826367, + -7.820508, + -7.849023, + -7.870898, + -7.877832, + -7.880664, + -7.88623, + -7.871094, + -7.883106, + -7.874902, + -7.854004, + -7.857519, + -7.869141, + -7.872461, + -7.901758, + -7.9125, + -7.905176, + -7.898926, + -7.867578, + -7.842676, + -7.813477, + -7.805957, + -7.763379, + -7.742676, + -7.723438, + -7.708886, + -7.707226, + -7.680859, + -7.664746, + -7.67207, + -7.678614, + -7.662598, + -7.665722, + -7.691406, + -7.688574, + -7.691309, + -7.716504, + -7.764355, + -7.766406, + -7.79082, + -7.796484, + -7.816309, + -7.866016, + -7.924023, + -8.006446, + -8.039844, + -8.067676, + -8.079004, + -8.092481, + -8.079004, + -8.066211, + -8.045801, + -8.002442, + -7.961133, + -7.948047, + -7.944238, + -7.944922, + -7.957129, + -7.990722, + -8.000977, + -8.004102, + -7.719824, + -7.71377, + -7.684863, + -7.661426, + -7.665332, + -7.656446, + -7.641894, + -7.635547, + -7.644141, + -7.660547, + -7.720019, + -7.738086, + -7.786914, + -7.860938, + -7.882715, + -7.879981, + -7.883984, + -7.898047, + -7.900977, + -7.87832, + -7.837793, + -7.841894, + -7.823828, + -7.792676, + -7.804883, + -7.808106, + -7.819336, + -7.835254, + -7.826172, + -7.821875, + -7.823242, + -7.787988, + -7.762207, + -7.751562, + -7.726855, + -7.692676, + -7.670313, + -7.640039, + -7.63711, + -7.639453, + -7.667285, + -7.65039, + -7.654492, + -7.638672, + -7.638965, + -7.638379, + -7.638183, + -7.655566, + -7.985645, + -7.972754, + -7.941114, + -7.89541, + -7.856348, + -7.822851, + -7.772266, + -7.712988, + -7.700586, + -7.678516, + -7.697656, + -7.680371, + -7.694824, + -7.689844, + -7.68711, + -7.689355, + -7.691406, + -7.69707, + -7.706152, + -7.718652, + -7.713086, + -7.715332, + -7.731348, + -7.745313, + -7.787793, + -7.816406, + -7.84209, + -7.88457, + -7.892969, + -7.901758, + -7.922363, + -7.934766, + -7.922266, + -7.926172, + -7.895313, + -7.872266, + -7.86123, + -7.841406, + -7.829297, + -7.847461, + -7.84043, + -7.832129, + -7.815722, + -7.798633, + -7.764746, + -7.748047, + -7.686817, + -7.669726, + -7.661718, + -7.630176, + -7.618946, + -7.607422, + -7.61709, + -7.647851, + -7.674317, + -7.693066, + -7.732031, + -7.733008, + -7.744141, + -7.771778, + -7.795313, + -7.806054, + -7.797461, + -7.766114, + -7.742187, + -7.72539, + -7.703125, + -7.727734, + -7.724902, + -7.728027, + -7.748633, + -7.78125, + -7.827149, + -7.888574, + -7.915918, + -7.956152, + -7.965332, + -7.957422, + -7.935742, + -7.924902, + -7.883398, + -7.836914, + -7.787402, + -7.744238, + -7.745606, + -7.718946, + -7.703809, + -7.709668, + -7.725879, + -7.717676, + -7.70625, + -7.703222, + -7.697168, + -7.671289, + -7.660645, + -7.645508, + -7.624317, + -7.624218, + -7.609668, + -7.616699, + -7.630078, + -7.619434, + -7.628125, + -7.63584, + -7.680469, + -7.699414, + -7.732031, + -7.731543, + -7.73457, + -7.748633, + -7.738672, + -7.758594, + -7.765918, + -7.763672, + -7.77373, + -7.813672, + -7.821973, + -7.885058, + -7.89541, + -7.92207, + -7.958301, + -7.946484, + -7.929394, + -7.92461, + -7.893457, + -7.868946, + -7.861914, + -7.81709, + -7.794629, + -7.748438, + -7.728906, + -7.739258, + -7.722754, + -7.716797, + -7.69209, + -7.654004, + -7.615234, + -7.595996, + -7.574707, + -7.568262, + -7.522656, + -7.493554, + -7.477734, + -7.466016, + -7.4625, + -7.503125, + -7.532715, + -7.515722, + -7.552832, + -7.578516, + -7.616992, + -7.633008, + -7.657519, + -7.708106, + -7.716016, + -7.752149, + -7.781054, + -7.801367, + -7.782519, + -7.791114, + -7.804883, + -7.844434, + -7.845215, + -7.845215, + -7.846094, + -7.82207, + -7.808789, + -7.832617, + -7.831641, + -7.802734, + -7.79043, + -7.770117, + -7.772168, + -7.785351, + -7.803222, + -7.790722, + -7.831934, + -7.836426, + -7.831446, + -7.853125, + -7.877442, + -7.86084, + -7.840234, + -7.797558, + -7.779981, + -7.766309, + -7.731054, + -7.713379, + -7.72705, + -7.740332, + -7.719922, + -7.74043, + -7.783691, + -7.826074, + -7.784375, + -7.771973, + -7.770703, + -7.777734, + -7.728418, + -7.694043, + -7.723438, + -7.738282, + -7.740039, + -7.741894, + -7.79082, + -7.792578, + -7.820703, + -7.818359, + -7.862598, + -7.875977, + -7.851465, + -7.846191, + -7.813574, + -7.805274, + -7.79834, + -7.782617, + -7.750195, + -7.751855, + -7.724317, + -7.702539, + -7.724414, + -7.708398, + -7.711426, + -7.724414, + -7.681641, + -7.655762, + -7.628418, + -7.601074, + -7.592871, + -7.581738, + -7.566602, + -7.566602, + -7.525195, + -7.527442, + -7.566309, + -7.576074, + -7.627734, + -7.648047, + -7.699414, + -7.74209, + -7.766211, + -7.807031, + -7.831446, + -7.836426, + -7.827149, + -7.825098, + -7.805957, + -7.82207, + -7.802149, + -7.81455, + -7.852637, + -7.855469, + -7.870508, + -7.859375, + -7.832519, + -7.795215, + -7.748145, + -7.719629, + -7.693359, + -7.680176, + -7.658203, + -7.653027, + -7.658203, + -7.672656, + -7.683301, + -7.7125, + -7.700488, + -7.64375, + -7.628125, + -7.603711, + -7.597558, + -7.608984, + -7.613183, + -7.625098, + -7.661133, + -7.669238, + -7.725, + -7.766797, + -7.811914, + -7.813183, + -7.824317, + -7.827442, + -7.820019, + -7.812207, + -7.813574, + -7.833106, + -7.843359, + -7.834082, + -7.83711, + -7.853809, + -7.851758, + -7.875195, + -7.883789, + -7.8875, + -7.912305, + -7.90166, + -7.91377, + -7.913282, + -7.91709, + -7.920215, + -7.9375, + -7.935938, + -7.935645, + -7.910938, + -7.849707, + -7.804004, + -7.755957, + -7.713574, + -7.667187, + -7.656054, + -7.631152, + -7.627344, + -7.647461, + -7.686328, + -7.717383, + -7.755859, + -7.769238, + -7.764649, + -7.770606, + -7.730957, + -7.673828, + -7.646289, + -7.635254, + -7.600782, + -7.622558, + -7.654981, + -7.702637, + -7.749902, + -7.798535, + -7.864746, + -7.931543, + -7.968066, + -7.999317, + -8.010254, + -7.981836, + -7.97832, + -7.991114, + -7.994434, + -7.969434, + -7.955859, + -7.929981, + -7.91875, + -7.894336, + -7.902734, + -7.918359, + -7.902442, + -7.897851, + -7.907617, + -7.928711, + -7.893457, + -7.877539, + -7.858789, + -7.852246, + -7.832617, + -7.780469, + -7.723633, + -7.690332, + -7.644531, + -7.594531, + -7.571973, + -7.531446, + -7.475977, + -7.416406, + -7.391699, + -7.417578, + -7.432129, + -7.449805, + -7.486817, + -7.564258, + -7.632031, + -7.703125, + -7.769531, + -7.838282, + -7.885742, + -7.932813, + -7.975488, + -7.998242, + -8.019336, + -8.016406, + -8.020117, + -8.04209, + -8.053516, + -8.005664, + -7.976172, + -7.921582, + -7.888477, + -7.854297, + -7.807519, + -7.743457, + -7.69961, + -7.642969, + -7.619531, + -7.620019, + -7.606836, + -7.601562, + -7.594531, + -7.589746, + -7.608106, + -7.604297, + -7.587012, + -7.584278, + -7.581934, + -7.52705, + -7.511914, + -7.509278, + -7.491894, + -7.503809, + -7.513574, + -7.556152, + -7.627734, + -7.670801, + -7.737305, + -7.815625, + -7.894238, + -7.926074, + -8.007031, + -8.014844, + -8.033398, + -8.025195, + -8.002539, + -7.983594, + -7.93584, + -7.904492, + -7.83584, + -7.771289, + -7.680664, + -7.620215, + -7.558886, + -7.550879, + -7.528906, + -7.513574, + -7.53623, + -7.553516, + -7.596386, + -7.666406, + -7.713477, + -7.770019, + -7.832324, + -7.836718, + -7.831934, + -7.828125, + -7.766602, + -7.718848, + -7.657715, + -7.574023, + -7.49082, + -7.379394, + -7.307324, + -7.281446, + -7.266211, + -7.297558, + -7.35791, + -7.422754, + -7.502442, + -7.635254, + -7.767285, + -7.918066, + -8.028906, + -8.151172, + -8.248535, + -8.263672, + -8.26211, + -8.231934, + -8.180274, + -8.067187, + -7.954687, + -7.814649, + -7.689844, + -7.568848, + -7.46211, + -7.417578, + -7.379199, + -7.400586, + -7.444434, + -7.510742, + -7.632617, + -7.744824, + -7.845996, + -7.946875, + -8.016211, + -8.048242, + -8.0625, + -8.03623, + -7.969336, + -7.864746, + -7.725098, + -7.566016, + -7.407422, + -7.276172, + -7.202246, + -7.154981, + -7.152539, + -7.173438, + -7.231543, + -7.372851, + -7.483594, + -7.652734, + -7.83877, + -7.97793, + -8.066406, + -8.113867, + -8.121289, + -8.083203, + -8.008594, + -7.898145, + -7.807422, + -7.685547, + -7.562695, + -7.468262, + -7.408203, + -7.429785, + -7.50166, + -7.61709, + -7.739258, + -7.850195, + -7.955469, + -8.04043, + -8.095215, + -8.099805, + -8.113965, + -8.04961, + -7.958496, + -7.810058, + -7.638477, + -7.47705, + -7.333203, + -7.241114, + -7.166894, + -7.170606, + -7.172168, + -7.221289, + -7.301758, + -7.440918, + -7.620703, + -7.826562, + -7.983398, + -8.136328, + -8.236718, + -8.258984, + -8.244336, + -8.185742, + -8.09541, + -7.982617, + -7.824512, + -7.620801, + -7.425879, + -7.25127, + -7.096778, + -7.01211, + -6.997168, + -7.009668, + -7.105176, + -7.258301, + -7.458398, + -7.731543, + -8.02959, + -8.299317, + -8.540234, + -8.712695, + -8.768164, + -8.731738, + -8.561133, + -8.290137, + -7.96709, + -7.600488, + -7.228614, + -6.88916, + -6.594141, + -6.39795, + -6.270898, + -6.303516, + -6.487793, + -6.754785, + -7.120019, + -7.525488, + -7.919531, + -8.315527, + -8.64541, + -8.86709, + -9.000586, + -9.005371, + -8.875, + -8.658691, + -8.392481, + -8.008789, + -7.601074, + -7.243262, + -6.891602, + -6.644824, + -6.525488, + -6.448047, + -6.515918, + -6.692774, + -6.928614, + -7.279687, + -7.68623, + -8.066406, + -8.420313, + -8.699805, + -8.876855, + -8.954394, + -8.915722, + -8.741406, + -8.493066, + -8.159961, + -7.790039, + -7.404981, + -7.043262, + -6.764062, + -6.569922, + -6.508789, + -6.580859, + -6.733984, + -6.95957, + -7.272363, + -7.614746, + -7.930762, + -8.267578, + -8.523828, + -8.67207, + -8.731738, + -8.662207, + -8.533203, + -8.337207, + -8.08789, + -7.816699, + -7.574023, + -7.344238, + -7.164453, + -7.071289, + -6.994824, + -7.030664, + -7.092871, + -7.240137, + -7.389746, + -7.539746, + -7.720508, + -7.895019, + -8.04043, + -8.138086, + -8.211426, + -8.186328, + -8.144043, + -8.04961, + -7.92373, + -7.789746, + -7.655274, + -7.489746, + -7.359082, + -7.314453, + -7.28125, + -7.303027, + -7.406543, + -7.520215, + -7.672168, + -7.810254, + -7.943164, + -8.050782, + -8.165918, + -8.191797, + -8.187988, + -8.099512, + -7.955469, + -7.782617, + -7.602637, + -7.475977, + -7.340039, + -7.24043, + -7.214942, + -7.224023, + -7.291894, + -7.469141, + -7.629981, + -7.835742, + -8.058301, + -8.21455, + -8.345313, + -8.445801, + -8.398828, + -8.328711, + -8.205469, + -8.008789, + -7.801855, + -7.61455, + -7.381152, + -7.226367, + -7.117676, + -7.045019, + -7.116016, + -7.240918, + -7.403222, + -7.657422, + -7.875683, + -8.094238, + -8.313183, + -8.469434, + -8.592774, + -8.667969, + -8.605274, + -8.475195, + -8.26582, + -7.977637, + -7.708008, + -7.404297, + -7.096191, + -6.855762, + -6.644726, + -6.512988, + -6.500293, + -6.614649, + -6.820215, + -7.128027, + -7.467285, + -7.84043, + -8.218066, + -8.521973, + -8.798438, + -8.966016, + -9.010351, + -8.925586, + -8.754199, + -8.49082, + -8.176465, + -7.787793, + -7.415039, + -7.079492, + -6.76416, + -6.592285, + -6.525, + -6.561914, + -6.719922, + -6.978906, + -7.294434, + -7.661817, + -8.039453, + -8.346386, + -8.605957, + -8.723242, + -8.708594, + -8.584961, + -8.393262, + -8.095703, + -7.758691, + -7.426465, + -7.093262, + -6.823242, + -6.644824, + -6.56045, + -6.569922, + -6.697168, + -6.86543, + -7.104394, + -7.427149, + -7.736914, + -8.056641, + -8.337207, + -8.586328, + -8.762988, + -8.823926, + -8.784668, + -8.687793, + -8.496778, + -8.21582, + -7.92959, + -7.634668, + -7.346386, + -7.106348, + -6.931446, + -6.867871, + -6.876758, + -6.983886, + -7.121191, + -7.344336, + -7.572168, + -7.811035, + -8.043946, + -8.188574, + -8.268652, + -8.29043, + -8.221386, + -8.086133, + -7.93916, + -7.733789, + -7.519824, + -7.310351, + -7.129004, + -7.046973, + -7.010351, + -7.067383, + -7.170117, + -7.307129, + -7.507422, + -7.741992, + -7.951953, + -8.140137, + -8.272461, + -8.301855, + -8.266309, + -8.161914, + -7.982813, + -7.788965, + -7.567285, + -7.329004, + -7.140039, + -7.002637, + -6.917774, + -6.933691, + -7.035058, + -7.222168, + -7.451953, + -7.71045, + -7.961133, + -8.186133, + -8.366504, + -8.478711, + -8.511621, + -8.474707, + -8.351758, + -8.143262, + -7.887988, + -7.61455, + -7.348926, + -7.119531, + -6.941699, + -6.864649, + -6.875098, + -6.932617, + -7.054981, + -7.243164, + -7.516992, + -7.811328, + -8.10039, + -8.353614, + -8.54082, + -8.623535, + -8.604102, + -8.553418, + -8.434668, + -8.263477, + -7.99668, + -7.713867, + -7.416016, + -7.151562, + -6.961133, + -6.862988, + -6.840527, + -6.939258, + -7.081934, + -7.281934, + -7.557129, + -7.826367, + -8.118164, + -8.387988, + -8.583301, + -8.691797, + -8.732226, + -8.667285, + -8.522851, + -8.300683, + -8.032519, + -7.787012, + -7.506446, + -7.246973, + -7.073535, + -6.962305, + -6.936621, + -6.969824, + -7.099121, + -7.286621, + -7.510351, + -7.747363, + -8.013574, + -8.261328, + -8.476953, + -8.622851, + -8.673438, + -8.660547, + -8.555176, + -8.396094, + -8.185254, + -7.945898, + -7.661718, + -7.42461, + -7.210645, + -7.076855, + -7.048828, + -7.088477, + -7.206152, + -7.38418, + -7.588183, + -7.806738, + -8.075879, + -8.259863, + -8.417871, + -8.52373, + -8.538379, + -8.468946, + -8.318066, + -8.124121, + -7.892481, + -7.64043, + -7.400293, + -7.199414, + -7.060351, + -7.011035, + -6.995215, + -7.091309, + -7.238965, + -7.402149, + -7.606348, + -7.851172, + -8.044922, + -8.196973, + -8.326953, + -8.341504, + -8.340332, + -8.300977, + -8.195019, + -8.076074, + -7.92959, + -7.780762, + -7.63789, + -7.530078, + -7.438477, + -7.39961, + -7.463672, + -7.513379, + -7.631348, + -7.772461, + -7.910547, + -8.006738, + -8.11709, + -8.187695, + -8.202637, + -8.165039, + -8.071484, + -7.971386, + -7.850977, + -7.710351, + -7.571778, + -7.463672, + -7.397656, + -7.328906, + -7.319434, + -7.355859, + -7.425683, + -7.514258, + -7.608691, + -7.722168, + -7.847558, + -7.941699, + -8.001953, + -8.039844, + -8.045703, + -7.995898, + -7.947363, + -7.862793, + -7.795313, + -7.722266, + -7.65127, + -7.609473, + -7.596778, + -7.595606, + -7.63955, + -7.728125, + -7.802246, + -7.897558, + -8.000683, + -8.105664, + -8.202344, + -8.282617, + -8.323633, + -8.294922, + -8.240234, + -8.15039, + -8.010254, + -7.875, + -7.710351, + -7.535156, + -7.39961, + -7.307226, + -7.232226, + -7.233203, + -7.246875, + -7.301074, + -7.388672, + -7.508789, + -7.63711, + -7.779297, + -7.914355, + -8.000683, + -8.094141, + -8.142383, + -8.149218, + -8.120508, + -8.04209, + -7.930274, + -7.795019, + -7.644531, + -7.503711, + -7.399512, + -7.316114, + -7.292774, + -7.273438, + -7.307031, + -7.388672, + -7.50459, + -7.635645, + -7.787598, + -7.930469, + -8.038183, + -8.13711, + -8.167871, + -8.180762, + -8.152344, + -8.122168, + -8.002246, + -7.863282, + -7.723438, + -7.592676, + -7.459375, + -7.355859, + -7.282813, + -7.258301, + -7.27461, + -7.26416, + -7.322851, + -7.41289, + -7.526367, + -7.625098, + -7.723828, + -7.807715, + -7.858106, + -7.871191, + -7.866406, + -7.858106, + -7.824512, + -7.804492, + -7.71377, + -7.672461, + -7.629004, + -7.581348, + -7.561035, + -7.587793, + -7.612402, + -7.691309, + -7.73916, + -7.810742, + -7.875586, + -7.94082, + -7.976953, + -8.005274, + -7.999023, + -7.968652, + -7.899317, + -7.843359, + -7.798047, + -7.709961, + -7.682226, + -7.640625, + -7.60039, + -7.587207, + -7.593946, + -7.593652, + -7.59961, + -7.614062, + -7.643946, + -7.697461, + -7.729883, + -7.768262, + -7.786426, + -7.821484, + -7.807422, + -7.778711, + -7.769531, + -7.722168, + -7.656836, + -7.602832, + -7.574512, + -7.523535, + -7.503711, + -7.468066, + -7.47627, + -7.533203, + -7.598926, + -7.672656, + -7.75918, + -7.860645, + -7.893848, + -7.946484, + -7.984278, + -7.99961, + -8.008886, + -7.985058, + -7.947558, + -7.891602, + -7.825195, + -7.731934, + -7.696386, + -7.616504, + -7.582519, + -7.552442, + -7.512207, + -7.54082, + -7.570996, + -7.650782, + -7.698242, + -7.771778, + -7.819629, + -7.876953, + -7.892481, + -7.900098, + -7.891699, + -7.839258, + -7.80205, + -7.75166, + -7.69834, + -7.619141, + -7.570215, + -7.540722, + -7.516309, + -7.535351, + -7.574023, + -7.649805, + -7.710742, + -7.786817, + -7.91543, + -8.01455, + -8.090918, + -8.130176, + -8.186523, + -8.203516, + -8.201953, + -8.137305, + -8.059961, + -7.995215, + -7.865234, + -7.736621, + -7.676953, + -7.608496, + -7.610645, + -7.595703, + -7.592578, + -7.62334, + -7.650488, + -7.689355, + -7.74043, + -7.842285, + -7.901855, + -7.920019, + -7.944629, + -7.94541, + -7.923242, + -7.894043, + -7.850195, + -7.815527, + -7.778125, + -7.69209, + -7.649414, + -7.631446, + -7.568946, + -7.563867, + -7.599414, + -7.603906, + -7.625977, + -7.663672, + -7.67627, + -7.742774, + -7.792578, + -7.81377, + -7.90205, + -7.925, + -7.94375, + -7.947168, + -7.959278, + -7.91123, + -7.909863, + -7.909375, + -7.912988, + -7.927246, + -7.91543, + -7.89668, + -7.898828, + -7.90957, + -7.903809, + -7.933008, + -7.973633, + -7.976367, + -7.966309, + -7.933984, + -7.9125, + -7.861914, + -7.787207, + -7.746582, + -7.709278, + -7.627832, + -7.573828, + -7.52627, + -7.496484, + -7.52832, + -7.52832, + -7.590332, + -7.698242, + -7.795606, + -7.845117, + -7.958106, + -8.000782, + -8.036426, + -8.096289, + -8.08916, + -8.075488, + -8.063379, + -7.978711, + -7.890039, + -7.861914, + -7.803809, + -7.752442, + -7.731934, + -7.716894, + -7.729102, + -7.780078, + -7.79834, + -7.836133, + -7.905566, + -7.916602, + -7.912012, + -7.963282, + -7.988379, + -8.001855, + -8.000683, + -7.959082, + -7.951465, + -7.903711, + -7.830566, + -7.804785, + -7.730762, + -7.638574, + -7.536817, + -7.45205, + -7.409082, + -7.40791, + -7.388379, + -7.45, + -7.544141, + -7.66582, + -7.735645, + -7.811328, + -7.909082, + -7.983398, + -8.040722, + -8.056054, + -8.008594, + -7.958691, + -7.863183, + -7.724218, + -7.670898, + -7.597558, + -7.541309, + -7.540332, + -7.496289, + -7.507324, + -7.564355, + -7.601172, + -7.673926, + -7.77295, + -7.84375, + -7.957519, + -8.066504, + -8.118164, + -8.160645, + -8.189649, + -8.146973, + -8.128809, + -8.019726, + -7.926953, + -7.82295, + -7.690527, + -7.567774, + -7.479883, + -7.414258, + -7.366309, + -7.377246, + -7.369531, + -7.393359, + -7.42793, + -7.464453, + -7.512598, + -7.578711, + -7.608398, + -7.654687, + -7.680957, + -7.66377, + -7.655957, + -7.66455, + -7.618359, + -7.579785, + -7.593554, + -7.575879, + -7.622168, + -7.641894, + -7.668457, + -7.718457, + -7.79082, + -7.864844, + -7.968946, + -8.088477, + -8.171484, + -8.257422, + -8.303906, + -8.339062, + -8.397461, + -8.394043, + -8.31084, + -8.260938, + -8.154004, + -8.004785, + -7.881543, + -7.751074, + -7.629004, + -7.525683, + -7.422168, + -7.323438, + -7.287695, + -7.254004, + -7.228027, + -7.288574, + -7.331348, + -7.376953, + -7.46084, + -7.544043, + -7.579687, + -7.69873, + -7.777539, + -7.843457, + -7.957031, + -8.008789, + -8.057324, + -8.140527, + -8.145313, + -8.178906, + -8.201465, + -8.166797, + -8.136133, + -8.108203, + -8.034375, + -7.998242, + -7.936914, + -7.87168, + -7.804004, + -7.743164, + -7.640625, + -7.583301, + -7.529981, + -7.495508, + -7.477832, + -7.458203, + -7.410547, + -7.445996, + -7.445215, + -7.445996, + -7.55, + -7.532715, + -7.59082, + -7.628516, + -7.663574, + -7.714258, + -7.789844, + -7.765137, + -7.838086, + -7.866211, + -7.830566, + -7.89043, + -7.897656, + -7.87832, + -7.880566, + -7.848242, + -7.830371, + -7.808594, + -7.741992, + -7.719434, + -7.709082, + -7.703125, + -7.756738, + -7.800977, + -7.87168, + -7.888574, + -7.934961, + -7.96416, + -8.006348, + -8.044922, + -8.061035, + -8.074317, + -8.026855, + -7.970313, + -7.930762, + -7.946289, + -7.897851, + -7.89707, + -7.853906, + -7.798828, + -7.76582, + -7.72207, + -7.69834, + -7.712305, + -7.638183, + -7.575098, + -7.558691, + -7.536817, + -7.54043, + -7.556446, + -7.566602, + -7.586133, + -7.614258, + -7.650683, + -7.650488, + -7.666797, + -7.69209, + -7.658008, + -7.678418, + -7.694336, + -7.705859, + -7.739844, + -7.766699, + -7.787305, + -7.87373, + -7.916797, + -7.939649, + -7.963477, + -7.977149, + -7.982031, + -7.97539, + -7.952442, + -7.936035, + -7.897461, + -7.857324, + -7.791992, + -7.718457, + -7.688965, + -7.608398, + -7.579297, + -7.544531, + -7.514649, + -7.530371, + -7.541016, + -7.577442, + -7.634082, + -7.66123, + -7.708789, + -7.761523, + -7.761817, + -7.806641, + -7.798047, + -7.79082, + -7.824414, + -7.75918, + -7.730274, + -7.743359, + -7.702246, + -7.689355, + -7.752734, + -7.73789, + -7.783106, + -7.852539, + -7.864062, + -7.923633, + -8.023926, + -8.036035, + -8.040039, + -8.100098, + -8.001758, + -7.985742, + -7.994824, + -7.906054, + -7.87373, + -7.854492, + -7.749902, + -7.716016, + -7.672656, + -7.584473, + -7.613867, + -7.565234, + -7.528027, + -7.535156, + -7.524121, + -7.537695, + -7.5875, + -7.649218, + -7.73955, + -7.800879, + -7.837402, + -7.906446, + -7.93711, + -7.963965, + -7.983984, + -7.946484, + -7.902734, + -7.829004, + -7.737305, + -7.668946, + -7.629883, + -7.560742, + -7.459375, + -7.429981, + -7.384375, + -7.358789, + -7.373926, + -7.406641, + -7.450488, + -7.540332, + -7.542285, + -7.595215, + -7.695313, + -7.711133, + -7.756446, + -7.868066, + -7.933691, + -8.010645, + -8.139258, + -8.164453, + -8.293457, + -8.418262, + -8.462012, + -8.550977, + -8.639844, + -8.595996, + -8.56211, + -8.489355, + -8.312305, + -8.166797, + -8.004492, + -7.726562, + -7.555664, + -7.343262, + -7.108594, + -7.005371, + -6.872656, + -6.779981, + -6.806641, + -6.827637, + -6.888867, + -7.031543, + -7.104981, + -7.263477, + -7.464062, + -7.614942, + -7.846875, + -8.011914, + -8.153809, + -8.253614, + -8.312793, + -8.340039, + -8.370898, + -8.338867, + -8.267578, + -8.208789, + -8.108594, + -8.03584, + -7.985938, + -7.934473, + -7.912305, + -7.928027, + -7.93955, + -7.996973, + -8.086621, + -8.108106, + -8.195019, + -8.251953, + -8.213379, + -8.248633, + -8.184278, + -8.04834, + -7.920313, + -7.697754, + -7.466602, + -7.346875, + -7.189844, + -7.110547, + -7.09043, + -7.091894, + -7.175782, + -7.284863, + -7.405664, + -7.593946, + -7.780371, + -7.929981, + -8.054785, + -8.127344, + -8.20127, + -8.195606, + -8.172558, + -8.142676, + -8.063477, + -7.966504, + -7.837695, + -7.723242, + -7.646386, + -7.611523, + -7.596778, + -7.639453, + -7.671289, + -7.719531, + -7.779004, + -7.845215, + -7.937793, + -8.00918, + -8.090137, + -8.112793, + -8.168164, + -8.184278, + -8.195215, + -8.20166, + -8.194824, + -8.125, + -8.061621, + -7.958008, + -7.829394, + -7.777246, + -7.672461, + -7.574218, + -7.48955, + -7.474023, + -7.448828, + -7.509082, + -7.537793, + -7.588282, + -7.652344, + -7.670801, + -7.693359, + -7.743066, + -7.749317, + -7.717969, + -7.691114, + -7.67959, + -7.632031, + -7.628614, + -7.602832, + -7.585938, + -7.634473, + -7.661718, + -7.694922, + -7.779297, + -7.856348, + -7.886914, + -8.022851, + -8.081446, + -8.09375, + -8.124805, + -8.095606, + -8.062695, + -8.035938, + -7.939258, + -7.853125, + -7.791699, + -7.645996, + -7.555371, + -7.540332, + -7.535938, + -7.521484, + -7.544922, + -7.584961, + -7.62207, + -7.658886, + -7.660254, + -7.683594, + -7.714942, + -7.730469, + -7.703711, + -7.717285, + -7.755469, + -7.789453, + -7.845606, + -7.907813, + -7.940918, + -7.972656, + -7.982519, + -7.954687, + -7.910547, + -7.87461, + -7.802149, + -7.775782, + -7.757519, + -7.729687, + -7.746289, + -7.721484, + -7.718554, + -7.702637, + -7.709961, + -7.660156, + -7.624805, + -7.547266, + -7.449218, + -7.414258, + -7.379492, + -7.378809, + -7.43711, + -7.488477, + -7.537598, + -7.632031, + -7.71123, + -7.873047, + -8.016406, + -8.129004, + -8.243359, + -8.344922, + -8.403418, + -8.446484, + -8.490527, + -8.461718, + -8.406934, + -8.261621, + -8.122754, + -7.962695, + -7.79082, + -7.631836, + -7.508496, + -7.391699, + -7.301074, + -7.233008, + -7.209766, + -7.217383, + -7.219629, + -7.281836, + -7.361817, + -7.410742, + -7.478418, + -7.561426, + -7.631836, + -7.752246, + -7.83916, + -7.932129, + -8.034375, + -8.062207, + -8.108496, + -8.152344, + -8.117774, + -8.078711, + -8.052149, + -7.96709, + -7.902246, + -7.85205, + -7.777246, + -7.750488, + -7.686426, + -7.634473, + -7.659668, + -7.646582, + -7.648926, + -7.654394, + -7.634278, + -7.667676, + -7.689649, + -7.694629, + -7.745215, + -7.763477, + -7.751855, + -7.776172, + -7.780566, + -7.80127, + -7.844629, + -7.851758, + -7.87793, + -7.91582, + -7.932324, + -7.986426, + -7.997558, + -7.982617, + -7.980371, + -7.921386, + -7.842871, + -7.74707, + -7.649512, + -7.575586, + -7.511621, + -7.435645, + -7.41416, + -7.390332, + -7.402832, + -7.453418, + -7.513282, + -7.612793, + -7.713282, + -7.768848, + -7.866406, + -7.986523, + -8.02793, + -8.119141, + -8.15205, + -8.163477, + -8.208008, + -8.197754, + -8.203809, + -8.217871, + -8.151855, + -8.106152, + -8.081446, + -7.994434 + ], + "sampleFreq": 64, + "triggerType": 2, + "version": 2 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 0, + "dropped": false + } + } + }` + rawData := &common_models.IotaData{} + err := json.Unmarshal([]byte(rawDataMsg_vib), rawData) + if err != nil { + log.Panicf("测试异常%s", err.Error()) + } + + nodeWorker := app.NewEtWorker() + nodeStageManage := stages.NewStageManager() + nodeStageManage.AddSource(nodeWorker.LoadCh()) + sinkRawHandler := et_sink.NewSinkRawHandler() + nodeStageManage.AddStages(sinkRawHandler.GetStage()) + infoHandler := et_Info.NewInfoHandler() + nodeStageManage.AddStages(infoHandler.GetStage()) + calcHandler := et_calc.NewCalcHandler() + nodeStageManage.AddStages(calcHandler.GetStage()) + sinkThemeHandler := et_sink.NewSinkThemeHandler() + nodeStageManage.AddStages(sinkThemeHandler.GetStage()) + + nodeStageManage.Run() + + log.Println("测试开始") + nodeWorker.ConsumerProcess(rawData) + time.Sleep(time.Second * 100) + log.Println("测试结束") + + //商用最终数据 + //{ + // "sensor_name" : "5-ZDZ1", + // "factor_name" : "梁体振动", + // "factor_proto_code" : "5002", + // "data" : { + // "pv" : 1.53, + // "trms" : 0.36, + // "ppv" : 2.784 + //}, + // "factor_proto_name" : "振动加速度", + // "factor" : 25, + // "collect_time" : "2024-04-16T10:56:45.000Z", + // "sensor" : 44017, + // "structure" : 2069, + // "iota_device" : [ + //"0fc481cb-4299-42b5-ac7a-be556e2e8175" + //], + //"create_time" : "2024-04-16T09:51:36.132Z" + //} + +} + +func TestNodeHandler_Cache_window2(t *testing.T) { + + rawDataMsg_WSD := `{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "5da9aa1b-05b7-4943-be57-dedb34f7a1bd", + "dimensionId": "76c75371-bb9a-4f71-a25d-58adf7938296", + "dimCapId": "f8e00449-7dad-4ca8-a4eb-0284784efa8f", + "capId": "d2add1b3-b21c-420b-82a4-e0c55ee3a019", + "deviceId": "ed0f1d94-49a9-415b-9336-f965a2b0a985", + "scheduleId": "8c83fb77-48f3-493b-9311-1b6b7b4fa7a7", + "taskId": "0e1c7d3d-257d-4763-a335-198aef0fc625", + "jobId": 2, + "jobRepeatId": 1, + "triggerTime": "2024-04-08T11:01:48.001+08:00", + "realTime": "2024-04-08T11:01:48.001+08:00", + "finishTime": "2024-04-08T11:01:48.001+08:00", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "humidy": 17.5, + "Temp": 17.8 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 1, + "dropped": false + } + } +}` + + rawDataMsg_WSD2 := `{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "5da9aa1b-05b7-4943-be57-dedb34f7a1bd", + "dimensionId": "76c75371-bb9a-4f71-a25d-58adf7938296", + "dimCapId": "f8e00449-7dad-4ca8-a4eb-0284784efa8f", + "capId": "d2add1b3-b21c-420b-82a4-e0c55ee3a019", + "deviceId": "ed0f1d94-49a9-415b-9336-f965a2b0a985", + "scheduleId": "8c83fb77-48f3-493b-9311-1b6b7b4fa7a7", + "taskId": "0e1c7d3d-257d-4763-a335-198aef0fc625", + "jobId": 2, + "jobRepeatId": 1, + "triggerTime": "2024-04-08T11:02:00.001+08:00", + "realTime": "2024-04-08T11:02:00.001+08:00", + "finishTime": "2024-04-08T11:02:00.001+08:00", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "humidy": 20.5, + "Temp": 20.8 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 1, + "dropped": false + } + } +}` + rawData := &common_models.IotaData{} + err := json.Unmarshal([]byte(rawDataMsg_WSD), rawData) + if err != nil { + log.Panicf("测试异常%s", err.Error()) + } + + rawData2 := &common_models.IotaData{} + err2 := json.Unmarshal([]byte(rawDataMsg_WSD2), rawData2) + if err2 != nil { + log.Panicf("测试异常%s", err.Error()) + } + + nodeWorker := app.NewEtWorker() + nodeStageManage := stages.NewStageManager() + nodeStageManage.AddSource(nodeWorker.LoadCh()) + 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()) + sinkThemeHandler := et_sink.NewSinkThemeHandler() + nodeStageManage.AddStages(sinkThemeHandler.GetStage()) + nodeStageManage.Run() + + log.Println("测试开始") + nodeWorker.ConsumerProcess(rawData) + time.Sleep(time.Second * 1) + nodeWorker.ConsumerProcess(rawData2) + time.Sleep(time.Second * 100) + log.Println("测试结束") +} + +func TestStageMode(t *testing.T) { + stageManager := stages.NewStageManager() + nodeWorker := app.NewEtWorker() + stageManager.AddSource(nodeWorker.LoadCh()) + + stage1 := stages.NewStage("加") + stageManager.AddStages(*stage1) + out := make(chan *common_models.ProcessData) + + stageManager.AddOut(out) + + stageManager.Run() +} + +func TestNodeHandler_Cache_window6(t *testing.T) { + + //温湿度测点2 + rawDataMsg_WSD := `{ + "userId": "77804162-837d-4ff9-96c0-beb8e8888f8e", + "thingId": "5da9aa1b-05b7-4943-be57-dedb34f7a1bd", + "dimensionId": "76c75371-bb9a-4f71-a25d-58adf7938296", + "dimCapId": "f8e00449-7dad-4ca8-a4eb-0284784efa8f", + "capId": "d2add1b3-b21c-420b-82a4-e0c55ee3a019", + "deviceId": "fdbc2d0f-da70-4bdd-865e-6d26b677bbaf", + "scheduleId": "8c83fb77-48f3-493b-9311-1b6b7b4fa7a7", + "taskId": "0e1c7d3d-257d-4763-a335-198aef0fc625", + "jobId": 2, + "jobRepeatId": 1, + "triggerTime": "2024-04-09T12:00:01.001+08:00", + "realTime": "2024-04-09T12:00:01.001+08:00", + "finishTime": "2024-04-09T12:00:01.001+08:00", + "seq": 0, + "released": false, + "data": { + "type": 1, + "data": { + "humidy": 50, + "Temp": 17.0 + }, + "result": { + "code": 0, + "msg": "", + "detail": null, + "errTimes": 1, + "dropped": false + } + } +}` + + rawData := &common_models.IotaData{} + err := json.Unmarshal([]byte(rawDataMsg_WSD), rawData) + rawData.Data.Data["Temp"] = 17.0 + if err != nil { + log.Panicf("测试异常%s", err.Error()) + } + + rawData2 := &common_models.IotaData{} + err2 := json.Unmarshal([]byte(rawDataMsg_WSD), rawData2) + rawData2.Data.Data["Temp"] = 19.0 + rawData2.TriggerTime = rawData.TriggerTime.Add(time.Minute) + if err2 != nil { + log.Panicf("测试异常%s", err.Error()) + } + + rawData3 := &common_models.IotaData{} + err3 := json.Unmarshal([]byte(rawDataMsg_WSD), rawData3) + rawData3.Data.Data["Temp"] = 21.0 + rawData3.TriggerTime = rawData.TriggerTime.Add(time.Minute * 2) + if err3 != nil { + log.Panicf("测试异常%s", err.Error()) + } + + rawData4 := &common_models.IotaData{} + err4 := json.Unmarshal([]byte(rawDataMsg_WSD), rawData4) + rawData4.Data.Data["Temp"] = 23.0 + rawData4.TriggerTime = rawData.TriggerTime.Add(time.Minute * 3) + if err4 != nil { + log.Panicf("测试异常%s", err.Error()) + } + + rawData5 := &common_models.IotaData{} + err5 := json.Unmarshal([]byte(rawDataMsg_WSD), rawData5) + rawData5.Data.Data["Temp"] = 10.0 + rawData5.TriggerTime = rawData.TriggerTime.Add(time.Minute * 4) + if err5 != nil { + log.Panicf("测试异常%s", err.Error()) + } + + rawData6 := &common_models.IotaData{} + err6 := json.Unmarshal([]byte(rawDataMsg_WSD), rawData6) + rawData6.Data.Data["Temp"] = 14.0 + rawData6.TriggerTime = rawData.TriggerTime.Add(time.Minute * 5) + if err6 != nil { + log.Panicf("测试异常%s", err.Error()) + } + + nodeWorker := app.NewEtWorker() + nodeStageManage := stages.NewStageManager() + nodeStageManage.AddSource(nodeWorker.LoadCh()) + 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()) + sinkThemeHandler := et_sink.NewSinkThemeHandler() + nodeStageManage.AddStages(sinkThemeHandler.GetStage()) + nodeStageManage.Run() + + log.Println("测试开始") + nodeWorker.ConsumerProcess(rawData) + nodeWorker.ConsumerProcess(rawData2) + nodeWorker.ConsumerProcess(rawData3) + nodeWorker.ConsumerProcess(rawData4) + nodeWorker.ConsumerProcess(rawData5) + nodeWorker.ConsumerProcess(rawData6) + time.Sleep(time.Second * 100) + log.Println("测试结束") +} + +func TestTwoData(t *testing.T) { + TestNodeHandler_Formula303(t) + TestNodeHandler_Cache_window6(t) + +} diff --git a/node/stages/stage.go b/node/stages/stage.go new file mode 100644 index 0000000..eb8d4c8 --- /dev/null +++ b/node/stages/stage.go @@ -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) +} diff --git a/node/stages/stageManage.go b/node/stages/stageManage.go new file mode 100644 index 0000000..d5dd02e --- /dev/null +++ b/node/stages/stageManage.go @@ -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...) +} diff --git a/node/stages/stage_test.go b/node/stages/stage_test.go new file mode 100644 index 0000000..91e4bb3 --- /dev/null +++ b/node/stages/stage_test.go @@ -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 +} diff --git a/script/t_device_sensor添加列 formula_id.sql b/script/t_device_sensor添加列 formula_id.sql new file mode 100644 index 0000000..2a51c0d --- /dev/null +++ b/script/t_device_sensor添加列 formula_id.sql @@ -0,0 +1 @@ +ALTER TABLE "t_device_sensor" ADD COLUMN formula_id INTEGER; \ No newline at end of file