et-go 20240919重建
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
8.5 KiB

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