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
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
|
||
|
}
|