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.

234 lines
7.5 KiB

1 month ago
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
}