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] if outKey == "" { log.Printf("设备[%s]字段[%s]无绑定的监测原型[%s-%s] 字段输出", device.IotaDeviceId, inK, protoObj.Name, protoObj.Code) continue } outUnit := protoObj.GetProtoItem(outKey).UnitName k := the.unitHelper.GetUnitTransK(inUnit, outUnit) if v, ok := inV.(float64); ok { outMap[outKey] = common_calc.Decimal(v*k, 6) } else { if inV == "nil" { //数据模拟工具出现的称重 lane异常值 跳过 continue } outMap[outKey] = inV } } //todo 测点多设备特殊处理.. phyData := map[string]any{} for k, v := range outMap { phyData[k] = v } if len(p.Stations[i].Info.Devices) == 1 { p.Stations[i].Data.ThemeData = outMap p.Stations[i].Data.PhyData = phyData 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("%f", 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.ReadCacheNew(tempStationId, itemName) //如果没有温度窗口 创建温度窗口 if !valid { log.Printf("应变[%s]创建关联的温补滑窗 [%d]-[%s]", device.IotaDeviceId, tempStationId, itemName) tempWindow = the.cacheServer.CreatFilterWindow(tempStationId, itemName, "strainRelated") } 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 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 }