diff --git a/README_files/microk8s部署.png b/README_files/microk8s部署.png new file mode 100644 index 0000000..b66d8d2 Binary files /dev/null and b/README_files/microk8s部署.png differ diff --git a/README_files/node下线自动注销.png b/README_files/node下线自动注销.png new file mode 100644 index 0000000..6ea4b34 Binary files /dev/null and b/README_files/node下线自动注销.png differ diff --git a/README_files/raw时序库.png b/README_files/raw时序库.png new file mode 100644 index 0000000..0f79da4 Binary files /dev/null and b/README_files/raw时序库.png differ 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 0000000..4a8cc25 Binary files /dev/null and b/README_files/t_device_sensor添加列 formula_id.png differ diff --git a/README_files/时序库bucket.png b/README_files/时序库bucket.png new file mode 100644 index 0000000..0badd48 Binary files /dev/null and b/README_files/时序库bucket.png differ diff --git a/README_files/演示运行et-0.png b/README_files/演示运行et-0.png new file mode 100644 index 0000000..6574ee6 Binary files /dev/null and b/README_files/演示运行et-0.png differ diff --git a/README_files/演示运行et-1.png b/README_files/演示运行et-1.png new file mode 100644 index 0000000..820ee6d Binary files /dev/null and b/README_files/演示运行et-1.png differ diff --git a/README_files/状态集副本数设置2及以上.png b/README_files/状态集副本数设置2及以上.png new file mode 100644 index 0000000..b50c79a Binary files /dev/null and b/README_files/状态集副本数设置2及以上.png differ 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