|
|
|
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, 5)
|
|
|
|
} else {
|
|
|
|
if inV == "nil" {
|
|
|
|
//数据模拟工具出现的称重 lane异常值 跳过
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|