41 changed files with 2445 additions and 1 deletions
			
			
		| @ -0,0 +1,56 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"fmt" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type IotaData struct { | ||||
|  | 	UserId      string    `json:"userId"` | ||||
|  | 	ThingId     string    `json:"thingId"` | ||||
|  | 	DimensionId string    `json:"dimensionId"` | ||||
|  | 	DimCapId    string    `json:"dimCapId"` | ||||
|  | 	CapId       string    `json:"capId"` | ||||
|  | 	DeviceId    string    `json:"deviceId"` | ||||
|  | 	ScheduleId  string    `json:"scheduleId"` | ||||
|  | 	TaskId      string    `json:"taskId"` | ||||
|  | 	JobId       int       `json:"jobId"` | ||||
|  | 	JobRepeatId int       `json:"jobRepeatId"` | ||||
|  | 	TriggerTime time.Time `json:"triggerTime"` | ||||
|  | 	RealTime    time.Time `json:"realTime"` | ||||
|  | 	FinishTime  time.Time `json:"finishTime"` | ||||
|  | 	Seq         int       `json:"seq"` | ||||
|  | 	Released    bool      `json:"released"` | ||||
|  | 	Data        Data      `json:"data"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // ReadTaskId 支持内部字段定义任务ID (_acq_number)
 | ||||
|  | func (the *IotaData) ReadTaskId() (taskId string) { | ||||
|  | 	taskId = the.TaskId | ||||
|  | 	//可能的覆盖
 | ||||
|  | 	dtype, ok := the.Data.Data["_data_type"] | ||||
|  | 	if ok && dtype == RawTypeVib { | ||||
|  | 		taskId = the.TriggerTime.Format("20060102150405") | ||||
|  | 	} else { | ||||
|  | 		if acq, ok := the.Data.Data["_acq_number"]; ok { | ||||
|  | 			taskId = the.TriggerTime.Format("20060102") + fmt.Sprintf("%v", acq) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return taskId | ||||
|  | } | ||||
|  | 
 | ||||
|  | type Data struct { | ||||
|  | 	Type   int            `json:"type"` | ||||
|  | 	Data   map[string]any `json:"data"` | ||||
|  | 	Result struct { | ||||
|  | 		Code     int    `json:"code"` | ||||
|  | 		Msg      string `json:"msg"` | ||||
|  | 		Detail   string `json:"detail"` | ||||
|  | 		ErrTimes int    `json:"errTimes"` | ||||
|  | 		Dropped  bool   `json:"dropped"` | ||||
|  | 	} `json:"result"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (the *Data) Success() bool { | ||||
|  | 	return the.Result.Code == 0 | ||||
|  | } | ||||
| @ -0,0 +1,9 @@ | |||||
|  | MIT License | ||||
|  | 
 | ||||
|  | Copyright (c) <year> <copyright holders> | ||||
|  | 
 | ||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||||
|  | 
 | ||||
|  | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
|  | 
 | ||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
| @ -1,2 +1,26 @@ | |||||
| # common_models | # common_models  通用结构体模块 | ||||
| 
 | 
 | ||||
|  | ### 开发语言和版本 | ||||
|  | golang ,版本 go1.22.0 | ||||
|  | 
 | ||||
|  | ### 平台支持 | ||||
|  | 安心云4.0 | ||||
|  | 
 | ||||
|  | ### 使用方式:   | ||||
|  | 1. 设置 go 环境   | ||||
|  | set  GOPRIVATE=gitea.ngaiot.com  | ||||
|  | 2. 查询go 版本   | ||||
|  |  go list -m -versions  gitea.anxinyun.cn/container/common_models   | ||||
|  | 如若 GOPRIVATE=gitea.ngaiot.com 生效,则会返回 common_models 的版本信息。   | ||||
|  | 如若无版本信息返回,有可能是没有 DevOps 的权限,也有可能环境变量设置没有生效。 | ||||
|  | 
 | ||||
|  | 异常情况处理:   | ||||
|  | 1. 无 https://gitea.anxinyun.cn/container 访问权限,可以联系下管理员。 | ||||
|  | 2. 环境变量设置不生效   | ||||
|  | 解决方法,在GoLand工具中进行设置:   | ||||
|  | 1)打开 Settings   | ||||
|  | 2)Go > GOROOT,Download Go SDK   | ||||
|  | 3)Go > Go Modules,设置环境变量 GOPRIVATE=gitea.ngaiot.com | ||||
|  | 
 | ||||
|  | ### 依赖包管理 | ||||
|  | common_models  如若有修改,必须需要同步升级  common_utils、et-go | ||||
| @ -0,0 +1,54 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"fmt" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type AggData struct { | ||||
|  | 	Date        time.Time | ||||
|  | 	SensorId    int | ||||
|  | 	StructId    int | ||||
|  | 	FactorId    int | ||||
|  | 	AggTypeId   int                // 聚集类型 : 10分钟/30分钟/3小时/6小时/12小时/时/日/周/月聚集
 | ||||
|  | 	AggMethodId int                // 聚集方法 : 平均值/最大值/最小值
 | ||||
|  | 	Agg         map[string]float64 // 聚集数据
 | ||||
|  | 	Changed     map[string]float64 // 变化量
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (a *AggData) _t() time.Time { | ||||
|  | 	return a.Date | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (a *AggData) _q() string { | ||||
|  | 	return fmt.Sprintf("agg:%d", a.SensorId) | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (a *AggData) _r() string { | ||||
|  | 	return fmt.Sprintf("[Structure:%d] [Station:%d]", a.StructId, a.SensorId) | ||||
|  | } | ||||
|  | 
 | ||||
|  | var typeDict = map[int]string{ | ||||
|  | 	2001: "d", | ||||
|  | 	2002: "w", | ||||
|  | 	2003: "m", | ||||
|  | 	2004: "y", | ||||
|  | 	2005: "h", | ||||
|  | 	2006: "10m", | ||||
|  | 	2007: "30m", | ||||
|  | 	2008: "3h", | ||||
|  | 	2009: "6h", | ||||
|  | 	2010: "12h", | ||||
|  | 	3001: "avg", | ||||
|  | 	3002: "max", | ||||
|  | 	3003: "min", | ||||
|  | 	3004: "mid", | ||||
|  | 	3005: "sum", | ||||
|  | } | ||||
|  | 
 | ||||
|  | func GetAggTypeName(t int) string { | ||||
|  | 	if val, ok := typeDict[t]; ok { | ||||
|  | 		return val | ||||
|  | 	} | ||||
|  | 	return "" | ||||
|  | } | ||||
| @ -0,0 +1,195 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"fmt" | ||||
|  | 	"gitea.anxinyun.cn/container/common_models/constant/redisKey" | ||||
|  | 	"log" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | const ( | ||||
|  | 	Iota_Alarm_Status_Resolved = "resolved" | ||||
|  | 	Iota_Alarm_Status_Firing   = "firing" | ||||
|  | 	Iota_Alarm_OutOfRange      = "OutOfRange" | ||||
|  | 	Iota_Alarm_LinkStatus      = "LinkStatus" | ||||
|  | 
 | ||||
|  | 	Alarm_Mode_Generation = "AlarmGeneration" | ||||
|  | 	//自动恢复
 | ||||
|  | 	Alarm_Mode_AutoElimination = "AlarmAutoElimination" | ||||
|  | 	// 告警类型
 | ||||
|  | 	Alarm_Type_Dtu_LinkStatus   = "2001" // DTU下线
 | ||||
|  | 	Alarm_Type_Device_Status    = "3001" | ||||
|  | 	Alarm_Type_Timeout          = "3003" | ||||
|  | 	Alarm_Type_Data_Parse_Error = "3004" | ||||
|  | 	Alarm_Type_OutRange         = "3005" | ||||
|  | 	Alarm_Type_Data_Interupt    = "3006" | ||||
|  | 	// 超阈值
 | ||||
|  | 	Alarm_Type_Over_Threshold = "3007" | ||||
|  | 	// 变化速率超阈值
 | ||||
|  | 	Alarm_Type_Over_ChangeRate_Threshold = "3008" | ||||
|  | 	Alarm_Type_OutRange_Legacy           = "5001" | ||||
|  | 
 | ||||
|  | 	// 告警码
 | ||||
|  | 	Alarm_Code_OutRange      = "30050001" | ||||
|  | 	Alarm_Code_OutRange_Iota = "5001" | ||||
|  | 	Alarm_Code_OffLine       = "20010001" | ||||
|  | 
 | ||||
|  | 	// 告警源类型:DTU
 | ||||
|  | 	Alarm_Source_DTU = 0 | ||||
|  | 	// 告警源类型: 设备
 | ||||
|  | 	Alarm_Source_Device = 1 | ||||
|  | 	// 告警源类型: 测点
 | ||||
|  | 	Alarm_Source_Station = 2 | ||||
|  | 
 | ||||
|  | 	// 发起者: et-recv
 | ||||
|  | 	Alarm_Sponsor_Recv = "et.recv" | ||||
|  | 
 | ||||
|  | 	// 发起者 et-analyze
 | ||||
|  | 	Alarm_Sponsor_Threshold = "et.analyze" | ||||
|  | 	Alarm_Sponsor_Operator  = "operator" | ||||
|  | 	// 智慧应用
 | ||||
|  | 	Alarm_Sponsor_SmartApp = "smart.app" | ||||
|  | 
 | ||||
|  | 	// Alarm 缓存相关常量
 | ||||
|  | 	Alarm_Cache_Prefix     = "alarm" | ||||
|  | 	Alarm_Cache_Prefix_Agg = "AggThreshold" | ||||
|  | 	ALARM_SOURCE_DEVICE    = 1 | ||||
|  | 	ALARM_SOURCE_STATION   = 2 | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type Alarm struct { | ||||
|  | 	IsAlarm   bool | ||||
|  | 	Code      string | ||||
|  | 	AlarmType string | ||||
|  | 	Level     int | ||||
|  | 	Content   string | ||||
|  | 	Sponsor   string | ||||
|  | } | ||||
|  | 
 | ||||
|  | // NewAlarmOverThreshold 超阈值告警
 | ||||
|  | func NewAlarmOverThreshold(level int, content string) *Alarm { | ||||
|  | 	codeMap := map[int]string{ | ||||
|  | 		1: "30070001", | ||||
|  | 		2: "30070002", | ||||
|  | 		3: "30070003", | ||||
|  | 		4: "30070004", | ||||
|  | 	} | ||||
|  | 	code, ok := codeMap[level] | ||||
|  | 	if !ok { | ||||
|  | 		log.Printf("告警内容[%s] 告警等级[%d], 未找到对应的告警码 \n", content, level) | ||||
|  | 		return nil | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	return &Alarm{ | ||||
|  | 		IsAlarm:   true, | ||||
|  | 		Code:      code, | ||||
|  | 		AlarmType: Alarm_Type_Over_Threshold, | ||||
|  | 		Level:     level, | ||||
|  | 		Content:   content, | ||||
|  | 		Sponsor:   Alarm_Sponsor_Threshold, | ||||
|  | 	} | ||||
|  | } | ||||
|  | 
 | ||||
|  | // NewAlarmRecoverThreshold 阈值恢复告警
 | ||||
|  | func NewAlarmRecoverThreshold() *Alarm { | ||||
|  | 	alarm := Alarm{ | ||||
|  | 		IsAlarm:   false, | ||||
|  | 		Code:      "30070001", | ||||
|  | 		AlarmType: Alarm_Type_Over_Threshold, | ||||
|  | 		Level:     0, | ||||
|  | 		Content:   "", | ||||
|  | 		Sponsor:   Alarm_Sponsor_Threshold, | ||||
|  | 	} | ||||
|  | 	return &alarm | ||||
|  | } | ||||
|  | 
 | ||||
|  | // NewOverChangingRateThreshold 变化速率超阈值告警
 | ||||
|  | func NewOverChangingRateThreshold(level int, content string) *Alarm { | ||||
|  | 	codeMap := map[int]string{ | ||||
|  | 		1: "30080001", | ||||
|  | 		2: "30080002", | ||||
|  | 		3: "30080003", | ||||
|  | 		4: "30080004", | ||||
|  | 	} | ||||
|  | 	code, ok := codeMap[level] | ||||
|  | 	if !ok { | ||||
|  | 		log.Printf("告警内容[%s] 告警等级[%d], 未找到对应的告警码 \n", content, level) | ||||
|  | 		return nil | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	alarm := Alarm{ | ||||
|  | 		IsAlarm:   true, | ||||
|  | 		Code:      code, | ||||
|  | 		AlarmType: Alarm_Type_Over_ChangeRate_Threshold, | ||||
|  | 		Level:     level, | ||||
|  | 		Content:   content, | ||||
|  | 		Sponsor:   Alarm_Sponsor_Threshold, | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	return &alarm | ||||
|  | } | ||||
|  | 
 | ||||
|  | // NewRecoverChangingRateThreshold 变化速率阈值恢复告警
 | ||||
|  | func NewRecoverChangingRateThreshold() *Alarm { | ||||
|  | 	alarm := Alarm{ | ||||
|  | 		IsAlarm:   false, | ||||
|  | 		Code:      "30080001", | ||||
|  | 		AlarmType: Alarm_Type_Over_ChangeRate_Threshold, | ||||
|  | 		Level:     0, | ||||
|  | 		Content:   "", | ||||
|  | 		Sponsor:   Alarm_Sponsor_Threshold, | ||||
|  | 	} | ||||
|  | 	return &alarm | ||||
|  | } | ||||
|  | 
 | ||||
|  | // AlarmRedisKey 格式:alarm:sourceType:sourceId, 如:alarm:2:100
 | ||||
|  | func AlarmRedisKey(sourceType int, sourceId string) string { | ||||
|  | 	return fmt.Sprintf("%s:%d:%s", redisKey.Threshold, sourceType, sourceId) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // AlarmRedisKey_Agg 格式:AggThreshold:structId:factorId, 如:AggThreshold:1:106
 | ||||
|  | func AlarmRedisKey_Agg(structId int, factorId int) string { | ||||
|  | 	return fmt.Sprintf("%s:%d:%d", redisKey.Agg_threshold, structId, factorId) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // ToAlarmMsg 将测点信息 -> 告警信息
 | ||||
|  | func (a *Alarm) ToAlarmMsg(stationInfo StationInfo, acqTime time.Time) AlarmMsg { | ||||
|  | 	if a.IsAlarm { | ||||
|  | 		return AlarmMsg{ | ||||
|  | 			MessageMode:   Alarm_Mode_Generation, | ||||
|  | 			StructureId:   stationInfo.StructureId, | ||||
|  | 			StructureName: stationInfo.StructureName, | ||||
|  | 			SourceId:      fmt.Sprintf("%d", stationInfo.Id), | ||||
|  | 			SourceName:    stationInfo.Name, | ||||
|  | 			AlarmTypeCode: a.AlarmType, | ||||
|  | 			AlarmCode:     a.Code, | ||||
|  | 			Content:       a.Content, | ||||
|  | 			AcqTime:       acqTime, | ||||
|  | 			SourceTypeId:  Alarm_Source_Station, | ||||
|  | 			Sponsor:       a.Sponsor, | ||||
|  | 		} | ||||
|  | 	} else { | ||||
|  | 		return AlarmMsg{ | ||||
|  | 			MessageMode:   Alarm_Mode_AutoElimination, | ||||
|  | 			StructureId:   stationInfo.StructureId, | ||||
|  | 			StructureName: stationInfo.StructureName, | ||||
|  | 			SourceId:      fmt.Sprintf("%d", stationInfo.Id), | ||||
|  | 			SourceName:    "", | ||||
|  | 			AlarmTypeCode: a.AlarmType, | ||||
|  | 			AlarmCode:     "", | ||||
|  | 			Content:       "", | ||||
|  | 			AcqTime:       acqTime, | ||||
|  | 			SourceTypeId:  Alarm_Source_Station, | ||||
|  | 			Sponsor:       a.Sponsor, | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | } | ||||
|  | 
 | ||||
|  | type ThresholdAlarmDetail struct { | ||||
|  | 	Level   int | ||||
|  | 	Content string | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (t ThresholdAlarmDetail) ToString() string { | ||||
|  | 	return t.Content | ||||
|  | } | ||||
| @ -0,0 +1,40 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type AlarmMsg struct { | ||||
|  | 	MessageMode   string         `json:"messageMode"` | ||||
|  | 	StructureId   int            `json:"structureId"` | ||||
|  | 	StructureName string         `json:"structureName"` | ||||
|  | 	SourceId      string         `json:"sourceId"` | ||||
|  | 	SourceName    string         `json:"sourceName"` | ||||
|  | 	AlarmTypeCode string         `json:"alarmTypeCode"` | ||||
|  | 	AlarmCode     string         `json:"alarmCode"` | ||||
|  | 	Content       string         `json:"content"` | ||||
|  | 	AcqTime       time.Time      `json:"time"` | ||||
|  | 	SourceTypeId  int            `json:"sourceTypeId"` // 0:DTU, 1:传感器, 2:测点
 | ||||
|  | 	Sponsor       string         `json:"sponsor"`      // 消息的发起者 "et.recv"
 | ||||
|  | 	Extras        map[string]any `json:"extras"` | ||||
|  | 	SubDevices    []string       `json:"subDevices"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type AlarmCode struct { | ||||
|  | 	Id       int    `json:"id"` | ||||
|  | 	Code     string `json:"code"` | ||||
|  | 	Name     string `json:"name"` | ||||
|  | 	TypeCode string `json:"typeCode"` | ||||
|  | 	Level    int    `json:"level"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *AlarmCode) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *AlarmCode) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | } | ||||
| @ -0,0 +1,208 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"container/ring" | ||||
|  | 	"encoding/json" | ||||
|  | 	"log" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type AnalyzeData struct { | ||||
|  | 	Raw     float64 `json:"raw"` | ||||
|  | 	IsValid bool    `json:"isValid"` | ||||
|  | 	Data    float64 `json:"data"` | ||||
|  | } | ||||
|  | type CacheWinSave struct { | ||||
|  | 	*CacheWindow | ||||
|  | 	AllData []AnalyzeData `json:"allData"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type expirationInfo struct { | ||||
|  | 	UpdateTime time.Time | ||||
|  | 	//过期秒数
 | ||||
|  | 	Duration float64 | ||||
|  | 	Expired  bool | ||||
|  | } | ||||
|  | 
 | ||||
|  | // CacheWindow
 | ||||
|  | // 存储大量测点缓存数据
 | ||||
|  | type CacheWindow struct { | ||||
|  | 	Id         string | ||||
|  | 	windowLen  int //窗体长度
 | ||||
|  | 	WindowSize int //窗体大小
 | ||||
|  | 	MethodId   int //滑窗方法
 | ||||
|  | 	LatestData any | ||||
|  | 	ring       *ring.Ring | ||||
|  | 	Params     FilterParams | ||||
|  | 	//过期控制
 | ||||
|  | 	Expire expirationInfo | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (c *CacheWindow) Size() int { | ||||
|  | 	return c.WindowSize | ||||
|  | } | ||||
|  | 
 | ||||
|  | // Len 获取窗口里有效长度(数据数量)
 | ||||
|  | func (c *CacheWindow) Len() int { | ||||
|  | 	return c.windowLen | ||||
|  | } | ||||
|  | func (c *CacheWindow) CheckExpiration() bool { | ||||
|  | 	if time.Now().Sub(c.Expire.UpdateTime).Seconds() > c.Expire.Duration { | ||||
|  | 		c.Expire.Expired = true | ||||
|  | 	} | ||||
|  | 	return c.Expire.Expired | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (c *CacheWindow) ToSaveCache() CacheWinSave { | ||||
|  | 	return CacheWinSave{ | ||||
|  | 		CacheWindow: c, | ||||
|  | 		AllData:     c.DeQueueAllAnalyzeData(), //追加存储的中间字段
 | ||||
|  | 	} | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (c *CacheWindow) MarshalBinary() (data []byte, err error) { | ||||
|  | 	// 序列化时包含新字段
 | ||||
|  | 	sCache := CacheWinSave{ | ||||
|  | 		CacheWindow: c, | ||||
|  | 		AllData:     c.DeQueueAllAnalyzeData(), //追加存储的中间字段
 | ||||
|  | 	} | ||||
|  | 	return json.Marshal(sCache) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (c *CacheWindow) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, c) | ||||
|  | } | ||||
|  | func NewCacheWindow(id string, size, methodId int, params FilterParams) CacheWindow { | ||||
|  | 	limit := 10 | ||||
|  | 	if size > limit { | ||||
|  | 		size = 10 | ||||
|  | 	} | ||||
|  | 	return CacheWindow{ | ||||
|  | 		Id:         id, | ||||
|  | 		WindowSize: size, | ||||
|  | 		MethodId:   methodId, | ||||
|  | 		ring:       ring.New(size), | ||||
|  | 		Params:     params, | ||||
|  | 		Expire: expirationInfo{ | ||||
|  | 			UpdateTime: time.Now(), | ||||
|  | 			Duration:   60 * 2, | ||||
|  | 			Expired:    false, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (c *CacheWindow) ReInitialRing() { | ||||
|  | 	defer func(*CacheWindow) { | ||||
|  | 		if r := recover(); r != nil { | ||||
|  | 			log.Println(r) | ||||
|  | 			log.Println(c.WindowSize) | ||||
|  | 		} | ||||
|  | 	}(c) | ||||
|  | 
 | ||||
|  | 	if c == nil { | ||||
|  | 		log.Printf("c=nil") | ||||
|  | 		return | ||||
|  | 	} | ||||
|  | 	c.ring = ring.New(c.WindowSize) | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (c *CacheWindow) EnQueue(d any) { | ||||
|  | 	if c.windowLen < c.WindowSize { | ||||
|  | 		c.windowLen += 1 | ||||
|  | 	} | ||||
|  | 	c.LatestData = d | ||||
|  | 	c.ring.Value = d | ||||
|  | 	c.ring = c.ring.Next() | ||||
|  | 	log.Printf("[%s]测点缓存[m=%d] %d/%d", c.Id, c.MethodId, c.WindowSize, c.ring.Len()) | ||||
|  | } | ||||
|  | func (c *CacheWindow) EnQueueAnalyzeData(d AnalyzeData) { | ||||
|  | 	if c.windowLen < c.WindowSize { | ||||
|  | 		c.windowLen += 1 | ||||
|  | 	} | ||||
|  | 	c.LatestData = d | ||||
|  | 	c.ring.Value = d | ||||
|  | 	c.ring = c.ring.Next() | ||||
|  | 	log.Printf("[%s]测点缓存[m=%d] %d/%d", c.Id, c.MethodId, c.WindowSize, c.ring.Len()) | ||||
|  | } | ||||
|  | func (c *CacheWindow) DeQueue() any { | ||||
|  | 	return c.ring.Prev().Value | ||||
|  | } | ||||
|  | func (c *CacheWindow) Latest() any { | ||||
|  | 	return c.ring.Prev().Value | ||||
|  | } | ||||
|  | func (c *CacheWindow) LatestByAnalyzeData() (AnalyzeData, bool) { | ||||
|  | 	var d AnalyzeData | ||||
|  | 	var valid bool | ||||
|  | 	if v, ok := c.ring.Prev().Value.(AnalyzeData); ok { | ||||
|  | 		d = v | ||||
|  | 		valid = true | ||||
|  | 	} | ||||
|  | 	return d, valid | ||||
|  | } | ||||
|  | func (c *CacheWindow) LatestByRange(size int) []any { | ||||
|  | 	var resp []any | ||||
|  | 	if size <= c.WindowSize { | ||||
|  | 		pre := c.ring.Prev() | ||||
|  | 		for i := 0; i < size; i++ { | ||||
|  | 			resp = append(resp, pre.Value) | ||||
|  | 			pre = pre.Prev() | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return resp | ||||
|  | } | ||||
|  | func (c *CacheWindow) DeQueueAll() []any { | ||||
|  | 	var all []any | ||||
|  | 	c.ring.Do(func(d any) { | ||||
|  | 		if d != nil { | ||||
|  | 			all = append(all, d) | ||||
|  | 		} | ||||
|  | 	}) | ||||
|  | 	return all | ||||
|  | } | ||||
|  | func (c *CacheWindow) DeQueueAllAnalyzeData() []AnalyzeData { | ||||
|  | 	Objs := c.DeQueueAll() | ||||
|  | 	var all []AnalyzeData | ||||
|  | 	for _, obj := range Objs { | ||||
|  | 		if obj == nil { | ||||
|  | 			continue | ||||
|  | 		} | ||||
|  | 		if v, ok := obj.(AnalyzeData); ok { | ||||
|  | 			all = append(all, v) | ||||
|  | 		} else { //类型不对 立即返回
 | ||||
|  | 			return all | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	return all | ||||
|  | } | ||||
|  | func (c *CacheWindow) DeQueueAllData() ([]float64, bool) { | ||||
|  | 	Objs := c.DeQueueAll() | ||||
|  | 	var all []float64 | ||||
|  | 	for _, obj := range Objs { | ||||
|  | 		if obj == nil { | ||||
|  | 			continue | ||||
|  | 		} | ||||
|  | 		if v, ok := obj.(AnalyzeData); ok { | ||||
|  | 			all = append(all, v.Data) | ||||
|  | 		} else { | ||||
|  | 			return all, false | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	return all, true | ||||
|  | } | ||||
|  | func (c *CacheWindow) DeQueueAllRaw() ([]float64, bool) { | ||||
|  | 	Objs := c.DeQueueAll() | ||||
|  | 	var all []float64 | ||||
|  | 	for _, obj := range Objs { | ||||
|  | 		if v, ok := obj.(AnalyzeData); ok { | ||||
|  | 			all = append(all, v.Raw) | ||||
|  | 		} else { | ||||
|  | 			return all, false | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	return all, true | ||||
|  | } | ||||
| @ -0,0 +1,6 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | const ( | ||||
|  | 	RawTypeVib  = "vib" | ||||
|  | 	RawTypeDiag = "diag" | ||||
|  | ) | ||||
| @ -0,0 +1,21 @@ | |||||
|  | package iotaScheme | ||||
|  | 
 | ||||
|  | // iota scheme 常量
 | ||||
|  | const ( | ||||
|  | 	// ModeResponse 周期
 | ||||
|  | 	ModeResponse string = "R" | ||||
|  | 	// ModeListen 监听
 | ||||
|  | 	ModeListen string = "L" | ||||
|  | 	// UnitSecond 秒
 | ||||
|  | 	UnitSecond string = "second" | ||||
|  | 	// UnitMinute 分钟
 | ||||
|  | 	UnitMinute string = "minute" | ||||
|  | 	// UnitHour 小时
 | ||||
|  | 	UnitHour string = "hour" | ||||
|  | 	// UnitDay 日
 | ||||
|  | 	UnitDay string = "day" | ||||
|  | 	// UnitWeek 周
 | ||||
|  | 	UnitWeek string = "week" | ||||
|  | 	// UnitMonth 月
 | ||||
|  | 	UnitMonth string = "month" | ||||
|  | ) | ||||
| @ -0,0 +1,11 @@ | |||||
|  | package logTag | ||||
|  | 
 | ||||
|  | const ( | ||||
|  | 	Station   = "s" | ||||
|  | 	Device    = "d" | ||||
|  | 	Thing     = "t" | ||||
|  | 	Structure = "st" | ||||
|  | 	Group     = "group" | ||||
|  | 	GroupItem = "group_item" | ||||
|  | 	Formula   = "formula" | ||||
|  | ) | ||||
| @ -0,0 +1,101 @@ | |||||
|  | package redisKey | ||||
|  | 
 | ||||
|  | const ( | ||||
|  | 	Station = "station" | ||||
|  | 
 | ||||
|  | 	Station_pm_extra = "station_extra" | ||||
|  | 
 | ||||
|  | 	Station_extra = "station_extras" | ||||
|  | 
 | ||||
|  | 	// 暂未使用
 | ||||
|  | 	Device = "device" | ||||
|  | 
 | ||||
|  | 	Formula = "formula" | ||||
|  | 
 | ||||
|  | 	Factor = "factor" | ||||
|  | 
 | ||||
|  | 	Proto = "proto" | ||||
|  | 
 | ||||
|  | 	Device_proto = "device-proto" | ||||
|  | 
 | ||||
|  | 	Group = "group" | ||||
|  | 
 | ||||
|  | 	Station_group = "sg" | ||||
|  | 
 | ||||
|  | 	Station_corr_group = "scg" | ||||
|  | 
 | ||||
|  | 	Threshold = "threshold" | ||||
|  | 
 | ||||
|  | 	Ration = "ration" | ||||
|  | 
 | ||||
|  | 	Filter = "filter" | ||||
|  | 
 | ||||
|  | 	Iota_device = "iota_device" | ||||
|  | 
 | ||||
|  | 	Iota_thing = "iota_thing" | ||||
|  | 
 | ||||
|  | 	Iota_meta = "iota_meta" | ||||
|  | 
 | ||||
|  | 	// 包含设备的测点ID数组 Array[Int]
 | ||||
|  | 	Device_stationIds = "device_stations" | ||||
|  | 
 | ||||
|  | 	// 包含设备的测点ID数组 Array[Int]
 | ||||
|  | 	Device_stationObjs = "device_stationObjs" | ||||
|  | 
 | ||||
|  | 	// thing对应的结构物配置信息
 | ||||
|  | 	Thing_struct = "thing_struct" | ||||
|  | 
 | ||||
|  | 	// 结构物对应监测因素配置信息
 | ||||
|  | 	Struct_factor = "struct_factor" | ||||
|  | 
 | ||||
|  | 	// 结构物对应监测因素别名配置信息
 | ||||
|  | 	Struct_factor_name = "struct_factor_name" | ||||
|  | 
 | ||||
|  | 	// thing对应的设备ID
 | ||||
|  | 	Thing_devices = "thing_devices" | ||||
|  | 
 | ||||
|  | 	// iota thing deploy
 | ||||
|  | 	Deploy = "deploy" | ||||
|  | 
 | ||||
|  | 	// iota 采集策略
 | ||||
|  | 	Scheme = "scheme" | ||||
|  | 
 | ||||
|  | 	Scheme_state = "scheme_state" | ||||
|  | 
 | ||||
|  | 	// 设备侧计算公式绑定关系
 | ||||
|  | 	Product_formula = "product_formula" | ||||
|  | 
 | ||||
|  | 	Agg_threshold = "AggThreshold" | ||||
|  | 
 | ||||
|  | 	Dyna_glt = "dyna_glt" | ||||
|  | 
 | ||||
|  | 	Report = "reportGenerate" | ||||
|  | 
 | ||||
|  | 	Report_template = "reportTemplate" | ||||
|  | 
 | ||||
|  | 	Structure = "structure" | ||||
|  | 
 | ||||
|  | 	Structure_extra = "structure_extra" | ||||
|  | 
 | ||||
|  | 	//单位转换
 | ||||
|  | 	Transform_units = "transform_units" | ||||
|  | 
 | ||||
|  | 	Alarm_code = "alarm_code" | ||||
|  | 
 | ||||
|  | 	// 滑窗缓存
 | ||||
|  | 	Cache_filter = "cache_filter" | ||||
|  | 
 | ||||
|  | 	CacheWindow = "cacheWindow" | ||||
|  | 
 | ||||
|  | 	Agg_warn_time = "agg_warn_time" | ||||
|  | 
 | ||||
|  | 	Agg_pm10_warn_time = "agg_pm10_warn_time" | ||||
|  | 
 | ||||
|  | 	Wise_value = "wise_value" | ||||
|  | 
 | ||||
|  | 	Project_struct = "project_struct" | ||||
|  | 
 | ||||
|  | 	Trace = "trace" | ||||
|  | 
 | ||||
|  | 	StationData = "stationData" | ||||
|  | ) | ||||
| @ -0,0 +1,13 @@ | |||||
|  | package settlementParam | ||||
|  | 
 | ||||
|  | const ( | ||||
|  | 	Base      = "base" | ||||
|  | 	Ref_base  = "ref_base" | ||||
|  | 	Ref_point = "ref_point" | ||||
|  | 	// 相邻差异沉降 - 分组参数中顺序字段
 | ||||
|  | 	Diff_pos = "pos" | ||||
|  | 	// 相邻差异沉降 - 测点监测数据字段
 | ||||
|  | 	Diff_field_x = "x" | ||||
|  | 	// 相邻差异沉降 - 差异沉降数据字段
 | ||||
|  | 	Diff_field = "diffX" | ||||
|  | ) | ||||
| @ -0,0 +1,14 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import "time" | ||||
|  | 
 | ||||
|  | type DataTrace interface { | ||||
|  | 	// 数据时间
 | ||||
|  | 	_t() time.Time | ||||
|  | 
 | ||||
|  | 	// 定位标记
 | ||||
|  | 	_q() string | ||||
|  | 
 | ||||
|  | 	// 定位标记
 | ||||
|  | 	_r() string | ||||
|  | } | ||||
| @ -0,0 +1,25 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type DataUnit struct { | ||||
|  | 	Name        string      `json:"name"` | ||||
|  | 	Dimension   string      `json:"dimension"` | ||||
|  | 	Description string      `json:"description"` | ||||
|  | 	Coef        float64     `json:"coef"` | ||||
|  | 	Base        bool        `json:"base"` | ||||
|  | 	Alternative interface{} `json:"alternative"` | ||||
|  | } | ||||
|  | type DataUnitArray []DataUnit | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *DataUnitArray) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *DataUnitArray) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | } | ||||
| @ -0,0 +1,107 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type DeviceData struct { | ||||
|  | 	DeviceId    string | ||||
|  | 	Name        string | ||||
|  | 	ThingId     string | ||||
|  | 	StructId    int | ||||
|  | 	TaskId      string | ||||
|  | 	AcqTime     time.Time | ||||
|  | 	RealTime    time.Time | ||||
|  | 	ErrCode     int | ||||
|  | 	Raw         map[string]any | ||||
|  | 	RawUnit     map[string]string | ||||
|  | 	DeviceInfo  DeviceInfo | ||||
|  | 	DimensionId string | ||||
|  | 	//数据类型 常见有 comm="" ,RawTypeVib="vib"
 | ||||
|  | 	DataType string | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (d *DeviceData) GetVibrationData() VibrationData { | ||||
|  | 	vibData := VibrationData{} | ||||
|  | 	if d.DataType == RawTypeVib { | ||||
|  | 		if v, ok := d.Raw["filterFreq"]; ok { | ||||
|  | 			if vv, ok := v.(float64); ok { | ||||
|  | 				vibData.FilterFreq = vv | ||||
|  | 			} | ||||
|  | 		} | ||||
|  | 
 | ||||
|  | 		if v, ok := d.Raw["sampleFreq"]; ok { | ||||
|  | 			if vv, ok := v.(float64); ok { | ||||
|  | 				vibData.SampleFreq = vv | ||||
|  | 			} | ||||
|  | 		} | ||||
|  | 
 | ||||
|  | 		if v, ok := d.Raw["gainAmplifier"]; ok { | ||||
|  | 			if vv, ok := v.(float64); ok { | ||||
|  | 				vibData.GainAmplifier = byte(vv) | ||||
|  | 			} | ||||
|  | 		} | ||||
|  | 
 | ||||
|  | 		if v, ok := d.Raw["version"]; ok { | ||||
|  | 			if vv, ok := v.(float64); ok { | ||||
|  | 				vibData.Version = byte(vv) | ||||
|  | 			} | ||||
|  | 		} | ||||
|  | 
 | ||||
|  | 		if v, ok := d.Raw["triggerType"]; ok { | ||||
|  | 			if vv, ok := v.(float64); ok { | ||||
|  | 				vibData.TriggerType = byte(vv) | ||||
|  | 			} | ||||
|  | 		} | ||||
|  | 
 | ||||
|  | 		if v, ok := d.Raw["physicalvalue"]; ok { | ||||
|  | 
 | ||||
|  | 			if vSlice, ok := v.([]any); ok { | ||||
|  | 				for _, vObj := range vSlice { | ||||
|  | 					if vv, ok := vObj.(float64); ok { | ||||
|  | 						vibData.Data = append(vibData.Data, vv) | ||||
|  | 					} | ||||
|  | 				} | ||||
|  | 
 | ||||
|  | 			} | ||||
|  | 
 | ||||
|  | 			//去直流
 | ||||
|  | 			if len(vibData.Data) > 0 { | ||||
|  | 				avg := func(dataArray []float64) float64 { | ||||
|  | 					sum := 0.0 | ||||
|  | 					for _, f := range dataArray { | ||||
|  | 						sum += f | ||||
|  | 					} | ||||
|  | 					return sum / float64(len(dataArray)) | ||||
|  | 				}(vibData.Data) //common_calc.GetAvg(vibData.Data)
 | ||||
|  | 
 | ||||
|  | 				for i := 0; i < len(vibData.Data); i++ { | ||||
|  | 					vibData.Data[i] = vibData.Data[i] - avg | ||||
|  | 				} | ||||
|  | 			} | ||||
|  | 		} | ||||
|  | 
 | ||||
|  | 	} | ||||
|  | 	return vibData | ||||
|  | } | ||||
|  | 
 | ||||
|  | // VibrationData 振动数据
 | ||||
|  | type VibrationData struct { | ||||
|  | 	Version       byte | ||||
|  | 	SampleFreq    float64 | ||||
|  | 	FilterFreq    float64 | ||||
|  | 	GainAmplifier byte | ||||
|  | 	TriggerType   byte | ||||
|  | 	Data          []float64 // 原始波形数据
 | ||||
|  | 	Unit          string | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (v *VibrationData) FormatParams() map[string]any { | ||||
|  | 	return map[string]any{ | ||||
|  | 		"sampleFreq":    v.SampleFreq, | ||||
|  | 		"version":       v.Version, | ||||
|  | 		"filterFreq":    v.FilterFreq, | ||||
|  | 		"gainAmplifier": v.GainAmplifier, | ||||
|  | 		"triggerType":   v.TriggerType, | ||||
|  | 	} | ||||
|  | } | ||||
| @ -0,0 +1,23 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import "encoding/json" | ||||
|  | 
 | ||||
|  | type DeviceFactorProto struct { | ||||
|  | 	Formula  int               `json:"formula"` | ||||
|  | 	FieldVal map[string]string `json:"field_val"` | ||||
|  | 	//MultiFormula   interface{}       `json:"multi_formula"`
 | ||||
|  | 	//MultiFields    interface{}       `json:"multi_fields"`
 | ||||
|  | 	//UnitConversion interface{}       `json:"unit_conversion"`
 | ||||
|  | 	//纪录  数据输入输出的转换系数
 | ||||
|  | 	FieldValUnitK map[string]float64 | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *DeviceFactorProto) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *DeviceFactorProto) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | } | ||||
| @ -0,0 +1,8 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | type DeviceInfo struct { | ||||
|  | 	Id         string     `json:"id"` | ||||
|  | 	Name       string     `json:"name"` | ||||
|  | 	Structure  Structure  `json:"structure"` | ||||
|  | 	DeviceMeta DeviceMeta `json:"device_meta"` | ||||
|  | } | ||||
| @ -0,0 +1,71 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | 	"fmt" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type DeviceMeta struct { | ||||
|  | 	Id           string           `json:"id"` | ||||
|  | 	Name         string           `json:"name"` | ||||
|  | 	Model        string           `json:"model"` | ||||
|  | 	Properties   []iotaProperty   `json:"properties"` | ||||
|  | 	Capabilities []iotaCapability `json:"capabilities"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // "meta" : {
 | ||||
|  | // "windspeed" : "风速(m/s)",
 | ||||
|  | // "pm25" : "PM2.5(ug/m³)",
 | ||||
|  | // "pm10" : "PM10(ug/m³)",
 | ||||
|  | // "noise" : "噪声(db)",
 | ||||
|  | // "winddir" : "风向(°)",
 | ||||
|  | // "humidity" : "湿度(%)",
 | ||||
|  | // "temp" : "温度(℃)"
 | ||||
|  | // }
 | ||||
|  | 
 | ||||
|  | func (m *DeviceMeta) GetOutputProps() (out map[string]string) { | ||||
|  | 	out = make(map[string]string) | ||||
|  | 	if len(m.Capabilities) == 0 { | ||||
|  | 		return | ||||
|  | 	} | ||||
|  | 	for _, property := range m.Capabilities[0].Properties { | ||||
|  | 		info := fmt.Sprintf("%s(%s)", property.ShowName, property.Unit) | ||||
|  | 		out[property.Name] = info | ||||
|  | 	} | ||||
|  | 	return | ||||
|  | } | ||||
|  | func (m *DeviceMeta) GetOutputUnit() (out map[string]string) { | ||||
|  | 	out = make(map[string]string) | ||||
|  | 	if len(m.Capabilities) == 0 { | ||||
|  | 		return | ||||
|  | 	} | ||||
|  | 	for _, property := range m.Capabilities[0].Properties { | ||||
|  | 		out[property.Name] = property.Unit | ||||
|  | 	} | ||||
|  | 	return | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *DeviceMeta) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *DeviceMeta) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | 
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | type iotaCapability struct { | ||||
|  | 	CapabilityCategoryId int            `json:"capabilityCategoryId"` | ||||
|  | 	Id                   string         `json:"id"` | ||||
|  | 	Name                 string         `json:"name"` | ||||
|  | 	Properties           []iotaProperty `json:"properties"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type iotaProperty struct { | ||||
|  | 	Category string `json:"category"` | ||||
|  | 	Name     string `json:"name"` | ||||
|  | 	ShowName string `json:"showName"` | ||||
|  | 	Unit     string `json:"unit"` | ||||
|  | } | ||||
| @ -0,0 +1,17 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import "time" | ||||
|  | 
 | ||||
|  | // EsGroupTheme 分组主题数据结构体
 | ||||
|  | type EsGroupTheme struct { | ||||
|  | 	Structure       int            `json:"structure"` | ||||
|  | 	GroupId         int            `json:"group_id"` | ||||
|  | 	GroupName       string         `json:"group_name"` | ||||
|  | 	Factor          int            `json:"factor"` | ||||
|  | 	FactorName      string         `json:"factor_name"` | ||||
|  | 	FactorProtoCode string         `json:"factor_proto_code"` | ||||
|  | 	FactorProtoName string         `json:"factor_proto_name"` | ||||
|  | 	Data            map[string]any `json:"data"` | ||||
|  | 	CollectTime     time.Time      `json:"collect_time"` | ||||
|  | 	CreateTime      time.Time      `json:"create_time"` | ||||
|  | } | ||||
| @ -0,0 +1,37 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import "time" | ||||
|  | 
 | ||||
|  | type EsRaw struct { | ||||
|  | 	StructId       int               `json:"structId"` | ||||
|  | 	IotaDeviceName string            `json:"iota_device_name"` | ||||
|  | 	Data           map[string]any    `json:"data"` | ||||
|  | 	CollectTime    time.Time         `json:"collect_time"` | ||||
|  | 	Meta           map[string]string `json:"meta"` | ||||
|  | 	IotaDevice     string            `json:"iota_device"` | ||||
|  | 	CreateTime     time.Time         `json:"create_time"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type EsRawResp struct { | ||||
|  | 	Took     int  `json:"took"` | ||||
|  | 	TimedOut bool `json:"timed_out"` | ||||
|  | 	Shards   struct { | ||||
|  | 		Total      int `json:"total"` | ||||
|  | 		Successful int `json:"successful"` | ||||
|  | 		Skipped    int `json:"skipped"` | ||||
|  | 		Failed     int `json:"failed"` | ||||
|  | 	} `json:"_shards"` | ||||
|  | 	Hits struct { | ||||
|  | 		Total    int      `json:"total"` | ||||
|  | 		MaxScore float64  `json:"max_score"` | ||||
|  | 		Hits     []HitRaw `json:"hits"` | ||||
|  | 	} `json:"hits"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type HitRaw struct { | ||||
|  | 	Index  string  `json:"_index"` | ||||
|  | 	Type   string  `json:"_type"` | ||||
|  | 	Id     string  `json:"_id"` | ||||
|  | 	Score  float64 `json:"_score"` | ||||
|  | 	Source EsRaw   `json:"_source"` | ||||
|  | } | ||||
| @ -0,0 +1,41 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import "time" | ||||
|  | 
 | ||||
|  | type EsTheme struct { | ||||
|  | 	SensorName      string         `json:"sensor_name"` | ||||
|  | 	FactorName      string         `json:"factor_name"` | ||||
|  | 	FactorProtoCode string         `json:"factor_proto_code"` | ||||
|  | 	Data            map[string]any `json:"data"` | ||||
|  | 	FactorProtoName string         `json:"factor_proto_name"` | ||||
|  | 	Factor          int            `json:"factor"` | ||||
|  | 	CollectTime     time.Time      `json:"collect_time"` | ||||
|  | 	Sensor          int            `json:"sensor"` | ||||
|  | 	Structure       int            `json:"structure"` | ||||
|  | 	IotaDevice      []string       `json:"iota_device"` | ||||
|  | 	CreateTime      time.Time      `json:"create_time"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type EsThemeResp struct { | ||||
|  | 	Took     int  `json:"took"` | ||||
|  | 	TimedOut bool `json:"timed_out"` | ||||
|  | 	Shards   struct { | ||||
|  | 		Total      int `json:"total"` | ||||
|  | 		Successful int `json:"successful"` | ||||
|  | 		Skipped    int `json:"skipped"` | ||||
|  | 		Failed     int `json:"failed"` | ||||
|  | 	} `json:"_shards"` | ||||
|  | 	Hits struct { | ||||
|  | 		Total    int        `json:"total"` | ||||
|  | 		MaxScore float64    `json:"max_score"` | ||||
|  | 		Hits     []HitTheme `json:"hits"` | ||||
|  | 	} `json:"hits"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type HitTheme struct { | ||||
|  | 	Index  string  `json:"_index"` | ||||
|  | 	Type   string  `json:"_type"` | ||||
|  | 	Id     string  `json:"_id"` | ||||
|  | 	Score  float64 `json:"_score"` | ||||
|  | 	Source EsTheme `json:"_source"` | ||||
|  | } | ||||
| @ -0,0 +1,47 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import "time" | ||||
|  | 
 | ||||
|  | type EsVbRaw struct { | ||||
|  | 	isFloat        bool           //是否拍扁
 | ||||
|  | 	StructId       int            `json:"structId"` | ||||
|  | 	IotaDeviceName string         `json:"iota_device_name"` | ||||
|  | 	Param          map[string]any `json:"param"` | ||||
|  | 	Data           map[string]any `json:"data"` | ||||
|  | 	CollectTime    time.Time      `json:"collect_time"` | ||||
|  | 	IotaDevice     string         `json:"iota_device"` | ||||
|  | 	CreateTime     time.Time      `json:"create_time"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // FlatMapDynamicVib 振动数据打散
 | ||||
|  | func (the *EsVbRaw) FlatMapDynamicVib() []EsVbRaw { | ||||
|  | 	var EsVbRaws []EsVbRaw | ||||
|  | 	if !the.isFloat { | ||||
|  | 		onceMill := 0.0 //毫秒间隔
 | ||||
|  | 		if sampleFreqObj, ok := the.Param["sampleFreq"]; ok { | ||||
|  | 			if sampleFreq, ok := sampleFreqObj.(float64); ok { | ||||
|  | 				onceMill = 1000 / sampleFreq | ||||
|  | 			} | ||||
|  | 		} | ||||
|  | 		if rawsObj, ok := the.Data["raw"]; ok { | ||||
|  | 			if raws, ok := rawsObj.([]float64); ok { | ||||
|  | 				for i, raw := range raws { | ||||
|  | 					onceTime := the.CollectTime.Add(time.Duration(onceMill*float64(i)) * time.Millisecond) | ||||
|  | 					esVbRaw := EsVbRaw{ | ||||
|  | 						isFloat:        true, | ||||
|  | 						StructId:       the.StructId, | ||||
|  | 						IotaDeviceName: the.IotaDeviceName, | ||||
|  | 						Param:          the.Param, | ||||
|  | 						Data:           map[string]any{"physicalvalue": raw}, | ||||
|  | 						CollectTime:    onceTime, | ||||
|  | 						IotaDevice:     the.IotaDevice, | ||||
|  | 						CreateTime:     the.CreateTime, | ||||
|  | 					} | ||||
|  | 					EsVbRaws = append(EsVbRaws, esVbRaw) | ||||
|  | 				} | ||||
|  | 			} | ||||
|  | 		} | ||||
|  | 
 | ||||
|  | 	} | ||||
|  | 	return EsVbRaws | ||||
|  | } | ||||
| @ -0,0 +1,78 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | 	"log" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type Factor struct { | ||||
|  | 	Id        int               `json:"id"` | ||||
|  | 	Name      string            `json:"name"` | ||||
|  | 	ProtoCode string            `json:"protoCode"` | ||||
|  | 	ProtoName string            `json:"protoName"` | ||||
|  | 	Items     []ProtoItem       `json:"items"` | ||||
|  | 	Units     map[string]string `json:"units"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (p *Factor) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(p) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (p *Factor) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, p) | ||||
|  | } | ||||
|  | 
 | ||||
|  | type ProtoItem struct { | ||||
|  | 	Id        int    `json:"id"` | ||||
|  | 	Name      string `json:"name"` | ||||
|  | 	FieldName string `json:"field_name"` | ||||
|  | 	UnitName  string `json:"unit_name"` | ||||
|  | 	Precision int    `json:"precision"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type Proto struct { | ||||
|  | 	Code  string      `json:"code"` | ||||
|  | 	Name  string      `json:"name"` | ||||
|  | 	Items []ProtoItem `json:"items"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (f *Factor) GetProtoItem(fieldName string) *ProtoItem { | ||||
|  | 	for _, item := range f.Items { | ||||
|  | 		if item.FieldName == fieldName { | ||||
|  | 			return &item | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	log.Printf("无匹配字段 %s 的ProtoItem", fieldName) | ||||
|  | 	return &ProtoItem{} | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetFieldNames 获取监测原型监测项
 | ||||
|  | func (f *Factor) GetFieldNames() []string { | ||||
|  | 	var names []string | ||||
|  | 	for _, protoItem := range f.Items { | ||||
|  | 		names = append(names, protoItem.FieldName) | ||||
|  | 	} | ||||
|  | 	return names | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (p *Proto) GetProtoItem(fieldName string) *ProtoItem { | ||||
|  | 	for _, item := range p.Items { | ||||
|  | 		if item.FieldName == fieldName { | ||||
|  | 			return &item | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	log.Printf("无匹配字段 %s 的ProtoItem", fieldName) | ||||
|  | 	return &ProtoItem{} | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (p *Proto) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(p) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (p *Proto) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, p) | ||||
|  | } | ||||
| @ -0,0 +1,67 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import "encoding/json" | ||||
|  | 
 | ||||
|  | const ( | ||||
|  | 	Filter_CalcMedian = 1 // 取中值
 | ||||
|  | 
 | ||||
|  | 	Filter_LimitAmp = 2 // 限幅
 | ||||
|  | 
 | ||||
|  | 	Filter_CalcMeanValue = 3 // 滑动平均
 | ||||
|  | 
 | ||||
|  | 	Filter_CalcStvMean = 4 // 方差判断平均
 | ||||
|  | 
 | ||||
|  | 	Filter_CalcWindow = 5 // 滤波算法
 | ||||
|  | 
 | ||||
|  | 	Filter_ExtreAverage = 6 // 去极值移动平均
 | ||||
|  | 
 | ||||
|  | 	Filter_WeightAverage = 7 // 加权滑动平均
 | ||||
|  | 
 | ||||
|  | 	Filter_MedianMean = 8 // 中值平均
 | ||||
|  | 
 | ||||
|  | 	Filter_RangeMean = 9 // 限幅平均
 | ||||
|  | 
 | ||||
|  | 	Filter_Interrupt = 10 // 中断
 | ||||
|  | 
 | ||||
|  | 	Filter_RandomReplacer = 9999 // 特殊算法: 数据替换
 | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type Filter struct { | ||||
|  | 	Items FilterItems | ||||
|  | } | ||||
|  | type FilterItems []FilterItem | ||||
|  | 
 | ||||
|  | func (t *FilterItems) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(t) | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (t *FilterItems) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, t) | ||||
|  | } | ||||
|  | 
 | ||||
|  | type FilterItem struct { | ||||
|  | 	Item           int            `json:"item"` | ||||
|  | 	FieldName      string         `json:"field_name"` | ||||
|  | 	Name           string         `json:"name"` | ||||
|  | 	MethodId       int            `json:"method"` | ||||
|  | 	Params         FilterParams   `json:"Params"` | ||||
|  | 	WindowSize     int            `json:"window_size"` | ||||
|  | 	Iswork         string         `json:"iswork"` //没用,默认都是false,只要存在都是启用的
 | ||||
|  | 	RInit          float64        `json:"RInit"` | ||||
|  | 	InternalParams InternalParams `json:"internal_params"` | ||||
|  | 	R              float64        `json:"R"` | ||||
|  | 	InvalidCount   int            `json:"InvalidCount"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type FilterParams struct { | ||||
|  | 	Kt string `json:"Kt"` | ||||
|  | 	Rt string `json:"Rt"` | ||||
|  | 	Dt string `json:"Dt"` | ||||
|  | 	Ru string `json:"Ru"` | ||||
|  | } | ||||
|  | type InternalParams struct { | ||||
|  | 	Kt string `json:"kt"` | ||||
|  | 	Rt string `json:"rt"` | ||||
|  | 	Dt string `json:"dt"` | ||||
|  | 	Ru string `json:"ru"` | ||||
|  | } | ||||
| @ -0,0 +1,55 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import "encoding/json" | ||||
|  | 
 | ||||
|  | type Formula struct { | ||||
|  | 	Id         int            `json:"id"` | ||||
|  | 	Expression string         `json:"expression"` | ||||
|  | 	Params     []FormulaParam `json:"params"` | ||||
|  | 	IoFields   IoFields       `json:"ioFields"` | ||||
|  | 	Type       string         `json:"type"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *Formula) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *Formula) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | type FormulaParam struct { | ||||
|  | 	Name    string  `json:"name"` | ||||
|  | 	Alias   string  `json:"alias"` | ||||
|  | 	Unit    string  `json:"unit"` | ||||
|  | 	Default float64 `json:"default"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type IoFields struct { | ||||
|  | 	Input  []ItemParam `json:"input"` | ||||
|  | 	Output []ItemParam `json:"output"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (the *IoFields) GetInFieldUnit(Field string) string { | ||||
|  | 	for _, InField := range the.Input { | ||||
|  | 		if InField.Name == Field { | ||||
|  | 			return InField.Unit | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return "" | ||||
|  | } | ||||
|  | func (the *IoFields) GetOutFieldUnit(Field string) string { | ||||
|  | 	for _, OutField := range the.Output { | ||||
|  | 		if OutField.Name == Field { | ||||
|  | 			return OutField.Unit | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return "" | ||||
|  | } | ||||
|  | 
 | ||||
|  | type ItemParam struct { | ||||
|  | 	Name string `json:"name"` | ||||
|  | 	Unit string `json:"unit"` | ||||
|  | } | ||||
| @ -0,0 +1,82 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | const ( | ||||
|  | 
 | ||||
|  | 	// 无公式
 | ||||
|  | 	FormulaType_None = 0 | ||||
|  | 
 | ||||
|  | 	// 振动
 | ||||
|  | 	FormulaType_vibCalc = 122 | ||||
|  | 
 | ||||
|  | 	// 爆破(三向)振动
 | ||||
|  | 	FormulaType_vibThreeCalc = 222 | ||||
|  | 
 | ||||
|  | 	// 振动索力识别公式
 | ||||
|  | 	FormulaType_cableRecognize = 120 | ||||
|  | 
 | ||||
|  | 	// 噪声
 | ||||
|  | 	FormulaType_vibNoise = 141 | ||||
|  | 
 | ||||
|  | 	// 动应变
 | ||||
|  | 	FormulaType_vibStrain = 148 | ||||
|  | 
 | ||||
|  | 	// 渗流计算
 | ||||
|  | 	FormulaType_seepage = 106 | ||||
|  | 
 | ||||
|  | 	// 应变花
 | ||||
|  | 	FormulaType_strainRosette = 207 | ||||
|  | 
 | ||||
|  | 	// 输电塔自动调平计算公式
 | ||||
|  | 	FormulaType_towerBalance = 212 | ||||
|  | 
 | ||||
|  | 	// 雷达物位计干滩计算公式
 | ||||
|  | 	FormulaType_radarDryBeach = 213 | ||||
|  | 
 | ||||
|  | 	// 单轴倾角仪测量双向角度
 | ||||
|  | 	FormulaType_comp2 = 214 | ||||
|  | 
 | ||||
|  | 	// 单向位移计测三向位移
 | ||||
|  | 	FormulaType_comp3 = 215 | ||||
|  | 
 | ||||
|  | 	// 进水量监测
 | ||||
|  | 	FormulaType_Inflow = 208 | ||||
|  | 
 | ||||
|  | 	// 流量/渗流量 计算
 | ||||
|  | 	FormulaType_Flow = 110 | ||||
|  | 
 | ||||
|  | 	// 管道轴向应力
 | ||||
|  | 	FormulaType_AxialStress = 216 | ||||
|  | 
 | ||||
|  | 	// 出水量计算
 | ||||
|  | 	FormulaType_Outflow = 136 | ||||
|  | 
 | ||||
|  | 	// 三角堰经验公式
 | ||||
|  | 	FormulaType_TriSeepageEmp = 137 | ||||
|  | 
 | ||||
|  | 	// 空气数据修正公式
 | ||||
|  | 	FormulaType_AirCorrect = 142 | ||||
|  | 
 | ||||
|  | 	// 插值计算
 | ||||
|  | 	FormulaType_Interpolation = 144 | ||||
|  | 
 | ||||
|  | 	// radar插值计算
 | ||||
|  | 	FormulaType_InterpolationRadar = 157 | ||||
|  | 	// radar插值计算2 (初值-测值)
 | ||||
|  | 	FormulaType_InterpolationRadar2 = 158 | ||||
|  | 
 | ||||
|  | 	// 差异沉降倾斜率
 | ||||
|  | 	FormulaType_InclinationSlope = 221 | ||||
|  | 
 | ||||
|  | 	// 减除值,取极值
 | ||||
|  | 	FormulaType_DefaultMaxMin = 156 | ||||
|  | 
 | ||||
|  | 	// 雷达物位计干滩计算公式
 | ||||
|  | 	FormulaType_radarDryBeach2 = 224 | ||||
|  | 
 | ||||
|  | 	// 北斗星空支撑轴力公式1
 | ||||
|  | 	FormulaType_axialSupportForce1 = 261 | ||||
|  | 	// 北斗星空支撑轴力公式2
 | ||||
|  | 	FormulaType_axialSupportForce2 = 262 | ||||
|  | 	// 东江大桥应变温补公式
 | ||||
|  | 	FormulaType_strainCompensationByTemperature = 303 | ||||
|  | ) | ||||
| @ -0,0 +1,11 @@ | |||||
|  | module gitea.anxinyun.cn/container/common_models | ||||
|  | 
 | ||||
|  | go 1.22.0 | ||||
|  | 
 | ||||
|  | require github.com/stretchr/testify v1.9.0 | ||||
|  | 
 | ||||
|  | require ( | ||||
|  | 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
|  | 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
|  | 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
|  | ) | ||||
| @ -0,0 +1,5 @@ | |||||
|  | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
|  | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
|  | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||
|  | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
|  | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| @ -0,0 +1,33 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"fmt" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type IotaAlarm struct { | ||||
|  | 	Id          string      `json:"id"` | ||||
|  | 	Status      string      `json:"status"` | ||||
|  | 	RepeatTimes int         `json:"repeatTimes"` | ||||
|  | 	StartsAt    time.Time   `json:"startsAt"` | ||||
|  | 	Severity    int         `json:"severity"` | ||||
|  | 	Labels      Labels      `json:"labels"` | ||||
|  | 	Annotations Annotations `json:"annotations"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (the *IotaAlarm) R_() string { | ||||
|  | 	return fmt.Sprintf("[t:%s][d:%s]", the.Labels.ThingId, the.Labels.DeviceId) | ||||
|  | } | ||||
|  | 
 | ||||
|  | type Labels struct { | ||||
|  | 	AlertName   string `json:"id"` | ||||
|  | 	DeviceId    string `json:"deviceId"` | ||||
|  | 	TaskId      string `json:"taskId"` | ||||
|  | 	DimensionId string `json:"dimensionId"` | ||||
|  | 	ThingId     string `json:"thingId"` | ||||
|  | 	UserId      string `json:"userId"` | ||||
|  | } | ||||
|  | type Annotations struct { | ||||
|  | 	Summary     string `json:"summary"` | ||||
|  | 	Description string `json:"description"` | ||||
|  | } | ||||
| @ -0,0 +1,76 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type IotaInstances struct { | ||||
|  | 	Instances map[string]IotaInstance `json:"instances"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *IotaInstances) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *IotaInstances) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | 
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | type IotaInstance struct { | ||||
|  | 	Instance DeployInstance `json:"instance"` | ||||
|  | 	Type     string         `json:"type"` //s.l or  s.d  or s.iota
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | type DeployInstance struct { | ||||
|  | 	DeviceMetaId string `json:"deviceMetaId"` | ||||
|  | 	Visibility   string `json:"visibility"` | ||||
|  | 	Name         string `json:"name"` | ||||
|  | 	From         ToFrom | ||||
|  | 	To           ToFrom | ||||
|  | } | ||||
|  | 
 | ||||
|  | type ToFrom struct { | ||||
|  | 	ShapeType      string `json:"shapeType"` | ||||
|  | 	OwnerShapeType string `json:"ownerShapeType"` | ||||
|  | 	OwnerSvgId     string `json:"ownerSvgId"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type DeviceTree struct { | ||||
|  | 	Node DeviceNode | ||||
|  | } | ||||
|  | type DeviceNode struct { | ||||
|  | 	Id    string | ||||
|  | 	Name  string | ||||
|  | 	Depth int | ||||
|  | 	pid   string | ||||
|  | 	Child []DeviceNode | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (the *DeviceNode) SearchSub(deviceId string) (subList []string) { | ||||
|  | 	if the.Id == deviceId { | ||||
|  | 		for _, child := range the.Child { | ||||
|  | 			subList = append(subList, child.Id) | ||||
|  | 		} | ||||
|  | 	} else { | ||||
|  | 		for _, child := range the.Child { | ||||
|  | 			subList = child.SearchSub(deviceId) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return subList | ||||
|  | } | ||||
|  | func (the *DeviceNode) SearchSubAll(deviceId string) (subList []string) { | ||||
|  | 	if the.Id == deviceId { | ||||
|  | 		for _, child := range the.Child { | ||||
|  | 			subList = append(subList, child.Id) | ||||
|  | 			subList = append(subList, child.SearchSub(child.Id)...) | ||||
|  | 		} | ||||
|  | 	} else { | ||||
|  | 		for _, child := range the.Child { | ||||
|  | 			subList = child.SearchSubAll(deviceId) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return subList | ||||
|  | } | ||||
| @ -0,0 +1,25 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type IotaDevice struct { | ||||
|  | 	Id           string     `json:"id"` | ||||
|  | 	Name         string     `json:"name"` | ||||
|  | 	Properties   string     `json:"properties"` | ||||
|  | 	DeviceMetaId string     `json:"deviceMetaId"` | ||||
|  | 	ThingId      string     `json:"thingId"` | ||||
|  | 	DeviceMeta   DeviceMeta `json:"deviceMeta"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *IotaDevice) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *IotaDevice) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | 
 | ||||
|  | } | ||||
| @ -0,0 +1,64 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | 	"gitea.anxinyun.cn/container/common_models/constant/iotaScheme" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type IotaScheme struct { | ||||
|  | 	Id        string     `json:"id"` | ||||
|  | 	Name      string     `json:"name"` | ||||
|  | 	Mode      string     `json:"mode"` | ||||
|  | 	Interval  int        `json:"interval"` | ||||
|  | 	Unit      string     `json:"unit"` | ||||
|  | 	BeginTime *time.Time `json:"beginTime"` | ||||
|  | 	EndTime   *time.Time `json:"endTime,omitempty"` | ||||
|  | 	Dimension Dimension  `json:"dimension"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | type Dimension struct { | ||||
|  | 	Id      string `json:"id"` | ||||
|  | 	ThingId string `json:"thingId"` | ||||
|  | 	Name    string `json:"name"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // SimpleEquals 调度模式一致(模式 & 间隔 & 时间单位)
 | ||||
|  | func (s *IotaScheme) SimpleEquals(other *IotaScheme) bool { | ||||
|  | 	return s.Mode == other.Mode && s.Interval == other.Interval && s.Unit == other.Unit | ||||
|  | } | ||||
|  | 
 | ||||
|  | // SimpleEqualsInt 周期调度的分钟数相等(周期 & 间隔 & 分钟)
 | ||||
|  | func (s *IotaScheme) SimpleEqualsInt(iv int) bool { | ||||
|  | 	return s.Mode == iotaScheme.ModeResponse && s.Interval == iv && s.Unit == iotaScheme.UnitMinute | ||||
|  | } | ||||
|  | 
 | ||||
|  | // IntervalSecs 间隔秒
 | ||||
|  | func (s *IotaScheme) IntervalSecs() int { | ||||
|  | 	switch s.Unit { | ||||
|  | 	case iotaScheme.UnitSecond: | ||||
|  | 		return s.Interval | ||||
|  | 	case iotaScheme.UnitMinute: | ||||
|  | 		return s.Interval * 60 | ||||
|  | 	case iotaScheme.UnitHour: | ||||
|  | 		return s.Interval * 60 * 60 | ||||
|  | 	case iotaScheme.UnitDay: | ||||
|  | 		return s.Interval * 60 * 60 * 24 | ||||
|  | 	case iotaScheme.UnitWeek: | ||||
|  | 		return s.Interval * 60 * 60 * 24 * 7 | ||||
|  | 	case iotaScheme.UnitMonth: | ||||
|  | 		return s.Interval * 60 * 60 * 24 * 30 | ||||
|  | 	default: | ||||
|  | 		return s.Interval | ||||
|  | 	} | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (s *IotaScheme) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(s) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (s *IotaScheme) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, s) | ||||
|  | } | ||||
| @ -0,0 +1,8 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | type ProcessData struct { | ||||
|  | 	//设备信息
 | ||||
|  | 	DeviceData DeviceData | ||||
|  | 	//测点信息
 | ||||
|  | 	Stations []Station //设备可对应多个测点
 | ||||
|  | } | ||||
| @ -0,0 +1,10 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | type NodeArgs struct { | ||||
|  | 	ID        string   // 节点标识符
 | ||||
|  | 	NodeType  string   // 节点类型:iota数据处理、聚集数据处理 类似的
 | ||||
|  | 	Status    string   // 节点状态:例如健康状态、负载情况、可用性等信息
 | ||||
|  | 	Resources string   // 节点资源情况:CPU、内存、存储等资源的容量和使用情况等信息
 | ||||
|  | 	Addr      string   // 网络地址
 | ||||
|  | 	ThingIds  []string // 处理的thingId
 | ||||
|  | } | ||||
| @ -0,0 +1,146 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | 	"errors" | ||||
|  | 	"fmt" | ||||
|  | 	"gitea.anxinyun.cn/container/common_models/constant/logTag" | ||||
|  | 	"gitea.anxinyun.cn/container/common_models/constant/settlementParam" | ||||
|  | 	"log" | ||||
|  | 	"strconv" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | var group36 = ` | ||||
|  | RedisKey = group:36 的值: | ||||
|  | { | ||||
|  |   "id": 36, | ||||
|  |   "name": "上游沉降", | ||||
|  |   "group_type": "202", | ||||
|  |   "items": [ | ||||
|  |     { | ||||
|  |       "sensor": 26, | ||||
|  |       "params_value": { | ||||
|  |         "base": false | ||||
|  |       } | ||||
|  |     }, | ||||
|  |     { | ||||
|  |       "sensor": 43, | ||||
|  |       "params_value": { | ||||
|  |         "base": false | ||||
|  |       } | ||||
|  |     }, | ||||
|  |     { | ||||
|  |       "sensor": 192, | ||||
|  |       "params_value": { | ||||
|  |         "base": true | ||||
|  |       } | ||||
|  |     } | ||||
|  |   ], | ||||
|  |   "params": { | ||||
|  |     "ref_base": 193, | ||||
|  |     "ref_point": 23 | ||||
|  |   } | ||||
|  | } | ||||
|  | ` | ||||
|  | 
 | ||||
|  | type StationGroup struct { | ||||
|  | 	Id        int         `json:"id"` | ||||
|  | 	Name      string      `json:"name"` | ||||
|  | 	GroupType string      `json:"group_type"` | ||||
|  | 	Items     []GroupItem `json:"items"` | ||||
|  | 	Params    map[string]interface{} | ||||
|  | } | ||||
|  | 
 | ||||
|  | // AllCorrItems 分组的关联项 = Items + 该分组的级联 ref_base、ref_point(可能有多层)
 | ||||
|  | func (g *StationGroup) AllCorrItems() []GroupItem { | ||||
|  | 	var allCorrItems []GroupItem | ||||
|  | 	for _, item := range g.Items { | ||||
|  | 		allCorrItems = append(allCorrItems, item.CorrItems()...) | ||||
|  | 	} | ||||
|  | 	return allCorrItems | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetSettlementBaseItem 获取沉降分组的基点
 | ||||
|  | func (g *StationGroup) GetSettlementBaseItem() *GroupItem { | ||||
|  | 	for _, item := range g.Items { | ||||
|  | 		if item.ParamsValue[settlementParam.Base] == true { | ||||
|  | 			return &item | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return nil | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (g *StationGroup) R() string { | ||||
|  | 	return fmt.Sprintf("[%s:%d]", logTag.Group, g.Id) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (g *StationGroup) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(g) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (g *StationGroup) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, g) | ||||
|  | } | ||||
|  | 
 | ||||
|  | type GroupItem struct { | ||||
|  | 	StationId   int                    `json:"sensor"` | ||||
|  | 	ParamsValue map[string]interface{} `json:"params_value"` | ||||
|  | 	SubItems    map[string]GroupItem   `json:"subItems,omitempty"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetDoubleParam 返回param的float64值
 | ||||
|  | func (g *GroupItem) GetDoubleParam(key string) (float64, error) { | ||||
|  | 	value := g.ParamsValue[key] | ||||
|  | 	switch v := value.(type) { | ||||
|  | 	case int: | ||||
|  | 		return float64(v), nil | ||||
|  | 	case float64: | ||||
|  | 		return v, nil | ||||
|  | 	case string: | ||||
|  | 		if floatValue, err := strconv.ParseFloat(v, 64); err == nil { | ||||
|  | 			return floatValue, nil | ||||
|  | 		} else { | ||||
|  | 			log.Printf("[GroupItem.getDoubleParam] key=%s value=%v ,value parse to float64 error: %v\n", key, value, err) | ||||
|  | 			return 0.0, err | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	var noKeyMsg = fmt.Sprintf("[GroupItem.getDoubleParam] no key=%s\n", key) | ||||
|  | 	log.Println(noKeyMsg) | ||||
|  | 	return 0.0, errors.New(noKeyMsg) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // CorrItems 基点的级联参考基点和参考测点
 | ||||
|  | func (g *GroupItem) CorrItems() []GroupItem { | ||||
|  | 	if g.SubItems == nil { | ||||
|  | 		return []GroupItem{*g} | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	var result []GroupItem | ||||
|  | 	for _, item := range g.SubItems { | ||||
|  | 		result = append(result, item.CorrItems()...) | ||||
|  | 	} | ||||
|  | 	result = append(result, *g) | ||||
|  | 	return result | ||||
|  | } | ||||
|  | 
 | ||||
|  | // StationGroupInfo 测点的分组信息(对应 ET3.0 的 secure_station_group )
 | ||||
|  | type StationGroupInfo struct { | ||||
|  | 	StationId int                    `json:"station"` | ||||
|  | 	GroupId   int                    `json:"group"` | ||||
|  | 	Params    map[string]interface{} `json:"params"` | ||||
|  | 	Name      string                 `json:"name"` | ||||
|  | 	GroupType string                 `json:"group_type"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (g *StationGroupInfo) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(g) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (g *StationGroupInfo) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, g) | ||||
|  | } | ||||
| @ -0,0 +1,181 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | 	"fmt" | ||||
|  | 	"gitea.anxinyun.cn/container/common_models/constant/logTag" | ||||
|  | 	"math" | ||||
|  | 	"strings" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type mStation struct { | ||||
|  | 	Name        string | ||||
|  | 	Id          int | ||||
|  | 	Structure   Structure | ||||
|  | 	Factor      Factor | ||||
|  | 	ManualData  bool `json:"manual_data"` | ||||
|  | 	Params      map[string]any | ||||
|  | 	Group       StationGroup | ||||
|  | 	DeviceProto DeviceProto | ||||
|  | 	CorrGroups  []StationGroup //关联的分组(沉降级联)
 | ||||
|  | 	Labels      string | ||||
|  | 	//CombineInfo // 测点数据组装信息
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | // Station 测点模型 = 基本信息 + 数据 + 阈值
 | ||||
|  | type Station struct { | ||||
|  | 	// 测点基本信息:名称、监测因素、监测原型、结构物、IOT-Things信息等
 | ||||
|  | 	Info StationInfo | ||||
|  | 	// 测点数据包括:设备数据 + 主题数据
 | ||||
|  | 	Data StationData | ||||
|  | 	// 测点阈值
 | ||||
|  | 	Threshold *Threshold | ||||
|  | } | ||||
|  | type StationData struct { | ||||
|  | 	//测点包含-计算后的设备单点数据
 | ||||
|  | 	DeviceCalcData map[string]any | ||||
|  | 	//测点最终数据(主题数据)
 | ||||
|  | 	ThemeData   map[string]any | ||||
|  | 	PyhData     map[string]any | ||||
|  | 	CollectTime time.Time | ||||
|  | 	AlarmLevel  int | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetThemeFields 获取主题数据的监测项
 | ||||
|  | func (s *StationData) GetThemeFields() []string { | ||||
|  | 	keys := make([]string, 0, len(s.ThemeData)) | ||||
|  | 	for key := range s.ThemeData { | ||||
|  | 		keys = append(keys, key) | ||||
|  | 	} | ||||
|  | 	return keys | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetProtoFields 获取监测原型的监测项
 | ||||
|  | func (s *Station) GetProtoFields() []string { | ||||
|  | 	var fields []string | ||||
|  | 	for _, protoItem := range s.Info.Factor.Items { | ||||
|  | 		fields = append(fields, protoItem.FieldName) | ||||
|  | 	} | ||||
|  | 	return fields | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetValidThemeData 获取有效的主题数据(数据规整为:非空float64)
 | ||||
|  | func (s *StationData) GetValidThemeData() (map[string]float64, bool) { | ||||
|  | 	processedData := make(map[string]float64) | ||||
|  | 
 | ||||
|  | 	for key, value := range s.ThemeData { | ||||
|  | 		switch v := value.(type) { | ||||
|  | 		case int: | ||||
|  | 			processedData[key] = float64(v) | ||||
|  | 		case float64: | ||||
|  | 			processedData[key] = v | ||||
|  | 		default: | ||||
|  | 			processedData[key] = math.NaN() | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	// 过滤掉值为NaN的条目
 | ||||
|  | 	filteredData := make(map[string]float64) | ||||
|  | 	for key, value := range processedData { | ||||
|  | 		if !math.IsNaN(value) { | ||||
|  | 			filteredData[key] = value | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	return filteredData, len(filteredData) > 0 | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (s *Station) LogMsg() string { | ||||
|  | 	logTagThing := fmt.Sprintf("[%s:%s][%s:%s]", logTag.Thing, s.Info.ThingId, logTag.Station, s.Info.Id) | ||||
|  | 
 | ||||
|  | 	var deviceLogs []string | ||||
|  | 	for _, device := range s.Info.Devices { | ||||
|  | 		deviceLogs = append(deviceLogs, device.LogMsg()) | ||||
|  | 	} | ||||
|  | 	deviceLogStr := strings.Join(deviceLogs, ",") | ||||
|  | 
 | ||||
|  | 	return logTagThing + deviceLogStr | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *Station) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *Station) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | 
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | type StationArrayObj []Station | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *StationArrayObj) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *StationArrayObj) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | type DeviceProto struct { | ||||
|  | 	Formula        int | ||||
|  | 	FieldVal       map[string]string | ||||
|  | 	MultiFormula   int | ||||
|  | 	multiFields    int | ||||
|  | 	UnitConversion map[string]float64 | ||||
|  | } | ||||
|  | 
 | ||||
|  | type StationInfo struct { | ||||
|  | 	Id            int            `json:"id"` | ||||
|  | 	Name          string         `json:"name"` | ||||
|  | 	StructureId   int            `json:"structure"` | ||||
|  | 	ThingId       string         `json:"thingId"` | ||||
|  | 	StructureName string         `json:"struct_name"` | ||||
|  | 	FactorId      int            `json:"factor"` | ||||
|  | 	IsManualData  bool           `json:"manual_data"` | ||||
|  | 	FormulaId     int            `json:"formula"` | ||||
|  | 	ParamsValue   map[string]any `json:"params_value"` | ||||
|  | 	Factor        Factor | ||||
|  | 	ProtoCode     string `json:"proto"` | ||||
|  | 	Proto         Proto | ||||
|  | 	Devices       []SecureStationDevice | ||||
|  | 	Labels        string | ||||
|  | 	CombineInfo   string | ||||
|  | 	Group         StationGroup   `json:"group,omitempty"` | ||||
|  | 	CorrGroups    []StationGroup `json:"corr_group_ids,omitempty"` // 关联的分组ID
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *StationInfo) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *StationInfo) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (the *StationInfo) GetDeviceIdArray() []string { | ||||
|  | 	var deviceIdArray []string | ||||
|  | 	for _, device := range the.Devices { | ||||
|  | 		deviceIdArray = append(deviceIdArray, device.IotaDeviceId) | ||||
|  | 	} | ||||
|  | 	return deviceIdArray | ||||
|  | } | ||||
|  | 
 | ||||
|  | type SecureStationDevice struct { | ||||
|  | 	FormulaId         int            `json:"formula_id"` //单设备测点 公式id redis里面原本没有 20240326后加
 | ||||
|  | 	Params            map[string]any `json:"params"` | ||||
|  | 	IotaDeviceId      string         `json:"iota_device_id"` | ||||
|  | 	IotaDeviceSerial  int            `json:"iota_device_serial"` | ||||
|  | 	FormulaInfo       Formula | ||||
|  | 	DeviceFactorProto DeviceFactorProto | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (s *SecureStationDevice) LogMsg() string { | ||||
|  | 	return fmt.Sprintf("[%s:%s]", logTag.Device, s.IotaDeviceId) | ||||
|  | } | ||||
| @ -0,0 +1,179 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"fmt" | ||||
|  | 	"github.com/stretchr/testify/assert" | ||||
|  | 	"testing" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | func Test_GroupItem_GetDoubleParam(t *testing.T) { | ||||
|  | 	groupItem := GroupItem{ | ||||
|  | 		StationId: 1, | ||||
|  | 		ParamsValue: map[string]interface{}{ | ||||
|  | 			"intVal":    1, | ||||
|  | 			"doubleVal": 5.0, | ||||
|  | 			"stringVal": "1", | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	// 正确的 int,double,string 数字
 | ||||
|  | 	intVal, err := groupItem.GetDoubleParam("intVal") | ||||
|  | 	doubleVal, err := groupItem.GetDoubleParam("doubleVal") | ||||
|  | 	stringVal, err := groupItem.GetDoubleParam("string") | ||||
|  | 	assert.IsTypef(t, float64(1), intVal, "val should be of type float64") | ||||
|  | 	assert.IsTypef(t, float64(1), doubleVal, "val should be of type float64") | ||||
|  | 	assert.IsTypef(t, float64(1), stringVal, "val should be of type float64") | ||||
|  | 
 | ||||
|  | 	// 错误的数字字符串,返回 (0.0,err) 测试
 | ||||
|  | 	groupItem.ParamsValue["stringVal"] = "dddd" | ||||
|  | 	errVal, err := groupItem.GetDoubleParam("stringVal") | ||||
|  | 	fmt.Printf("%v, %v \n", errVal, err) | ||||
|  | 	assert.NotNil(t, errVal, "val should be of type float64") | ||||
|  | 
 | ||||
|  | 	// 不存在的 key, 返回 (0.0,err) 测试
 | ||||
|  | 	noKeyVal, err := groupItem.GetDoubleParam("noKey") | ||||
|  | 	fmt.Printf("%v, %v \n", noKeyVal, err) | ||||
|  | 	assert.NotNil(t, noKeyVal, "val should be of type float64") | ||||
|  | } | ||||
|  | 
 | ||||
|  | func Test_GroupItem_CorrItems(t *testing.T) { | ||||
|  | 	// 假设有3个分组,分别为:group1,group2,group3, group1为基点分组, group2参考group1, group3参考group2
 | ||||
|  | 	// group1: id=1, 组内测点编号 1~5, 1为基点
 | ||||
|  | 	// group2: id=2, 组内测点编号 6~10, 6为基点
 | ||||
|  | 	// group3: id=3, 组内测点编号 11~15, 11为基点
 | ||||
|  | 
 | ||||
|  | 	//  group1: id=1, 组内测点编号 1~5; group1为基点分组
 | ||||
|  | 	group1_item1 := GroupItem{ | ||||
|  | 		StationId: 1, | ||||
|  | 		ParamsValue: map[string]interface{}{ | ||||
|  | 			"base": true, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 	group1_item5 := GroupItem{ | ||||
|  | 		StationId: 5, | ||||
|  | 		ParamsValue: map[string]interface{}{ | ||||
|  | 			"base": false, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 	//  group2: id=2, 组内测点编号 6~10; group2参考group1
 | ||||
|  | 	group2_item6 := GroupItem{ | ||||
|  | 		StationId: 6, | ||||
|  | 		ParamsValue: map[string]interface{}{ | ||||
|  | 			"base": true, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 	group2_item10 := GroupItem{ | ||||
|  | 		StationId: 10, | ||||
|  | 		ParamsValue: map[string]interface{}{ | ||||
|  | 			"base": false, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 	// 只有 baseItem 才设置 SubItems
 | ||||
|  | 	group2_item6.SubItems = map[string]GroupItem{ | ||||
|  | 		"ref_base":  group1_item1, | ||||
|  | 		"ref_point": group1_item5, | ||||
|  | 	} | ||||
|  | 	// group3: id=3, 组内测点编号 11~15; group3参考group2
 | ||||
|  | 	group3_item11 := GroupItem{ | ||||
|  | 		StationId: 11, | ||||
|  | 		ParamsValue: map[string]interface{}{ | ||||
|  | 			"base": true, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 	group3_item15 := GroupItem{ | ||||
|  | 		StationId: 15, | ||||
|  | 		ParamsValue: map[string]interface{}{ | ||||
|  | 			"base": false, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 	// 只有 baseItem 才设置 SubItems
 | ||||
|  | 	group3_item11.SubItems = map[string]GroupItem{ | ||||
|  | 		"ref_base":  group2_item6, | ||||
|  | 		"ref_point": group2_item10, | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	// ***************** 测试用例 ********************
 | ||||
|  | 	// SubItems = nil
 | ||||
|  | 	group1_corrItems := group1_item1.CorrItems() | ||||
|  | 	printMSG(group1_corrItems) | ||||
|  | 	assert.Equal(t, 1, len(group1_corrItems), "基点分组的基点,应该无级联分组") | ||||
|  | 	assert.Nil(t, group1_corrItems[0].SubItems, "基点分组的基点,应该无级联分组") | ||||
|  | 
 | ||||
|  | 	// SubItems = group1_item1 + group1_item5 + group2_item6
 | ||||
|  | 	group2_corrItems := group2_item6.CorrItems() | ||||
|  | 	printMSG(group2_corrItems) | ||||
|  | 	assert.Equal(t, 3, len(group2_corrItems), "group2的级联为group1, 应该返回3个分组项") | ||||
|  | 
 | ||||
|  | 	// SubItems = group1_item1 + group1_item5 + group2_item6 + group2_item10 + group3_item11
 | ||||
|  | 	group3_corrItems := group3_item11.CorrItems() | ||||
|  | 	printMSG(group3_corrItems) | ||||
|  | 	assert.Equal(t, 5, len(group3_corrItems), "group3级联group2, group2的级联group1, 应该返回5个分组项") | ||||
|  | 
 | ||||
|  | 	group3_point_corrItems := group3_item15.CorrItems() | ||||
|  | 	printMSG(group3_point_corrItems) | ||||
|  | 	assert.Equal(t, 1, len(group3_point_corrItems), "非基点项,应该无级联分组") | ||||
|  | 	assert.Nil(t, group3_point_corrItems[0].SubItems, "非基点项,应该无级联分组") | ||||
|  | 
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | func Test_StationGroup_AllCorrItems(t *testing.T) { | ||||
|  | 	params := map[string]interface{}{ | ||||
|  | 		"ref_base":  193, | ||||
|  | 		"ref_point": 23, | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	items := []GroupItem{ | ||||
|  | 		GroupItem{ | ||||
|  | 			StationId: 26, | ||||
|  | 			ParamsValue: map[string]interface{}{ | ||||
|  | 				"base": false, | ||||
|  | 			}, | ||||
|  | 		}, | ||||
|  | 		GroupItem{ | ||||
|  | 			StationId: 43, | ||||
|  | 			ParamsValue: map[string]interface{}{ | ||||
|  | 				"base": false, | ||||
|  | 			}, | ||||
|  | 		}, | ||||
|  | 		GroupItem{ | ||||
|  | 			StationId: 192, | ||||
|  | 			ParamsValue: map[string]interface{}{ | ||||
|  | 				"base": true, | ||||
|  | 			}, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 	items[2].SubItems = map[string]GroupItem{ | ||||
|  | 		"ref_base": GroupItem{ | ||||
|  | 			StationId: 193, | ||||
|  | 			ParamsValue: map[string]interface{}{ | ||||
|  | 				"base": true, | ||||
|  | 			}, | ||||
|  | 		}, | ||||
|  | 		"ref_point": GroupItem{ | ||||
|  | 			StationId: 23, | ||||
|  | 			ParamsValue: map[string]interface{}{ | ||||
|  | 				"base": false, | ||||
|  | 			}, | ||||
|  | 		}, | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	group := StationGroup{ | ||||
|  | 		Id:        36, | ||||
|  | 		Name:      "上游沉降", | ||||
|  | 		GroupType: "202", | ||||
|  | 		Items:     items, | ||||
|  | 		Params:    params, | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	corrItems := group.AllCorrItems() | ||||
|  | 	printMSG(corrItems) | ||||
|  | 	assert.Equal(t, 5, len(corrItems), "【上游沉降分组】有3个分组项(26,43,192*) + 有两个关联项(193,23), 应该返回5个分组项") | ||||
|  | } | ||||
|  | 
 | ||||
|  | // 打印 []GroupItem
 | ||||
|  | func printMSG(items []GroupItem) { | ||||
|  | 	for _, item := range items { | ||||
|  | 		fmt.Printf("%d,", item.StationId) | ||||
|  | 	} | ||||
|  | 	println("") | ||||
|  | } | ||||
| @ -0,0 +1,33 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | type Structure struct { | ||||
|  | 	ThingId   string  `json:"thingId"` | ||||
|  | 	Id        int     `json:"id"` | ||||
|  | 	Name      string  `json:"name"` | ||||
|  | 	SType     string  `json:"type"` | ||||
|  | 	OrgId     int     `json:"orgId"`     //结构物归属的组织ID
 | ||||
|  | 	Latitude  float64 `json:"latitude"`  //纬度
 | ||||
|  | 	Longitude float64 `json:"longitude"` //经度
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | type ThingStruct struct { | ||||
|  | 	ThingId string `json:"thingId"` | ||||
|  | 	Id      int    `json:"id"` | ||||
|  | 	Name    string `json:"name"` | ||||
|  | 	Type    string `json:"type"` | ||||
|  | 	OrgId   int    `json:"orgId"` | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *ThingStruct) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(m) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // redis序列化
 | ||||
|  | func (m *ThingStruct) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, m) | ||||
|  | } | ||||
| @ -0,0 +1,131 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | 	"strconv" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | // ThresholdItem 阈值项模型
 | ||||
|  | type ThresholdItem struct { | ||||
|  | 	Item        int     `json:"item"` // item -> t_factor_proto_item(id)
 | ||||
|  | 	FieldName   string  `json:"field_name"` | ||||
|  | 	Name        string  `json:"name"` | ||||
|  | 	Level       int     `json:"level"` | ||||
|  | 	Lower       float64 `json:"lower"` | ||||
|  | 	Upper       float64 `json:"upper"` | ||||
|  | 	Begin       *int    `json:"begin"`                  // 分时阈值起始时间(24小时制 0~24)
 | ||||
|  | 	End         *int    `json:"end"`                    // 分时阈值结束时间(24小时制 0~24)
 | ||||
|  | 	AggCategory *int    `json:"agg_category,omitempty"` // 在JSON编码时,如果此字段为空,则忽略该字段
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (t *ThresholdItem) IsTimeSegmented() bool { | ||||
|  | 	return t.Begin != nil && t.End != nil | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (t *ThresholdItem) RangeText() string { | ||||
|  | 	lowerText := "-" | ||||
|  | 	if t.Lower > -100000.0 { | ||||
|  | 		lowerText = strconv.FormatFloat(t.Lower, 'f', -1, 64) | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	upperText := "+" | ||||
|  | 	if t.Upper < 100000.0 { | ||||
|  | 		upperText = strconv.FormatFloat(t.Upper, 'f', -1, 64) | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	return lowerText + "~" + upperText | ||||
|  | } | ||||
|  | 
 | ||||
|  | // ThresholdItems 自定义类型,[]ThresholdItem的别名
 | ||||
|  | type ThresholdItems []ThresholdItem | ||||
|  | 
 | ||||
|  | func (t *ThresholdItems) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(t) | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (t *ThresholdItems) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, t) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // Threshold 阈值模型
 | ||||
|  | type Threshold struct { | ||||
|  | 	Items ThresholdItems | ||||
|  | } | ||||
|  | 
 | ||||
|  | // FindThresholdInRange 阈值判断
 | ||||
|  | func (t *Threshold) FindThresholdInRange(items []ThresholdItem, m float64) *ThresholdItem { | ||||
|  | 	for _, th := range items { | ||||
|  | 		if m > th.Lower && m <= th.Upper { | ||||
|  | 			return &th | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return nil | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetThresholdsByItem 根据监测项ID获取阈值
 | ||||
|  | func (t *Threshold) GetThresholdsByItem(items []ThresholdItem, itemID int) []ThresholdItem { | ||||
|  | 	var filtered []ThresholdItem | ||||
|  | 	for _, th := range items { | ||||
|  | 		if th.Item == itemID { | ||||
|  | 			filtered = append(filtered, th) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return filtered | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetThresholdsByTime 根据时间获取阈值
 | ||||
|  | func (t *Threshold) GetThresholdsByTime(date time.Time) []ThresholdItem { | ||||
|  | 	if t.Items == nil || len(t.Items) == 0 { | ||||
|  | 		return nil | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	if !t.Items[0].IsTimeSegmented() { | ||||
|  | 		return t.Items | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	hour := date.Hour() | ||||
|  | 	minute := date.Minute() | ||||
|  | 	sec := date.Second() | ||||
|  | 
 | ||||
|  | 	if hour == 0 && minute == 0 && sec == 0 { | ||||
|  | 		return t.filterByZero(t.Items, hour) | ||||
|  | 	} else if minute == 0 && sec == 0 { | ||||
|  | 		return t.filterByHour(t.Items, hour) | ||||
|  | 	} else { | ||||
|  | 		return t.filterByNormal(t.Items, hour) | ||||
|  | 	} | ||||
|  | } | ||||
|  | 
 | ||||
|  | // 0点时间的阈值模型
 | ||||
|  | func (t *Threshold) filterByZero(items []ThresholdItem, hour int) []ThresholdItem { | ||||
|  | 	var filteredItems []ThresholdItem | ||||
|  | 	for _, item := range items { | ||||
|  | 		if item.Begin != nil && *item.Begin == hour { | ||||
|  | 			filteredItems = append(filteredItems, item) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return filteredItems | ||||
|  | } | ||||
|  | 
 | ||||
|  | // 整点时间的阈值模型
 | ||||
|  | func (t *Threshold) filterByHour(items []ThresholdItem, hour int) []ThresholdItem { | ||||
|  | 	var filteredItems []ThresholdItem | ||||
|  | 	for _, item := range items { | ||||
|  | 		if item.Begin != nil && item.End != nil && *item.Begin < hour && *item.End >= hour { | ||||
|  | 			filteredItems = append(filteredItems, item) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return filteredItems | ||||
|  | } | ||||
|  | 
 | ||||
|  | // 常规时间的阈值模型
 | ||||
|  | func (t *Threshold) filterByNormal(items []ThresholdItem, hour int) []ThresholdItem { | ||||
|  | 	var filteredItems []ThresholdItem | ||||
|  | 	for _, item := range items { | ||||
|  | 		if item.Begin != nil && item.End != nil && *item.Begin <= hour && *item.End > hour { | ||||
|  | 			filteredItems = append(filteredItems, item) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return filteredItems | ||||
|  | } | ||||
| @ -0,0 +1,132 @@ | |||||
|  | package common_models | ||||
|  | 
 | ||||
|  | import ( | ||||
|  | 	"encoding/json" | ||||
|  | 	"strconv" | ||||
|  | 	"time" | ||||
|  | ) | ||||
|  | 
 | ||||
|  | // AggThresholdItem 阈值项模型
 | ||||
|  | type AggThresholdItem struct { | ||||
|  | 	Item        int     `json:"item"` // item -> t_factor_proto_item(id)
 | ||||
|  | 	FieldName   string  `json:"field_name"` | ||||
|  | 	Name        string  `json:"name"` | ||||
|  | 	Level       int     `json:"level"` | ||||
|  | 	Lower       float64 `json:"lower"` | ||||
|  | 	Upper       float64 `json:"upper"` | ||||
|  | 	Begin       *int    `json:"begin"`                  // 分时阈值起始时间(24小时制 0~24)
 | ||||
|  | 	End         *int    `json:"end"`                    // 分时阈值结束时间(24小时制 0~24)
 | ||||
|  | 	AggCategory *int    `json:"agg_category,omitempty"` // 在JSON编码时,如果此字段为空,则忽略该字段
 | ||||
|  | 	FuncType    *int    `json:"func_type,omitempty"`    // 在JSON编码时,如果此字段为空,则忽略该字段
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (t *AggThresholdItem) IsTimeSegmented() bool { | ||||
|  | 	return t.Begin != nil && t.End != nil | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (t *AggThresholdItem) RangeText() string { | ||||
|  | 	lowerText := "-" | ||||
|  | 	if t.Lower > -100000.0 { | ||||
|  | 		lowerText = strconv.FormatFloat(t.Lower, 'f', -1, 64) | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	upperText := "+" | ||||
|  | 	if t.Upper < 100000.0 { | ||||
|  | 		upperText = strconv.FormatFloat(t.Upper, 'f', -1, 64) | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	return lowerText + "~" + upperText | ||||
|  | } | ||||
|  | 
 | ||||
|  | // AggThresholdItems 自定义类型,[]AggThresholdItem 的别名
 | ||||
|  | type AggThresholdItems []AggThresholdItem | ||||
|  | 
 | ||||
|  | func (t *AggThresholdItems) MarshalBinary() (data []byte, err error) { | ||||
|  | 	return json.Marshal(t) | ||||
|  | } | ||||
|  | 
 | ||||
|  | func (t *AggThresholdItems) UnmarshalBinary(data []byte) error { | ||||
|  | 	return json.Unmarshal(data, t) | ||||
|  | } | ||||
|  | 
 | ||||
|  | // AggThreshold 阈值模型
 | ||||
|  | type AggThreshold struct { | ||||
|  | 	Items AggThresholdItems | ||||
|  | } | ||||
|  | 
 | ||||
|  | // FindThresholdInRange 阈值判断
 | ||||
|  | func (t *AggThreshold) FindThresholdInRange(items []AggThresholdItem, m float64) *AggThresholdItem { | ||||
|  | 	for _, th := range items { | ||||
|  | 		if m > th.Lower && m <= th.Upper { | ||||
|  | 			return &th | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return nil | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetThresholdsByItem 根据监测项ID获取阈值
 | ||||
|  | func (t *AggThreshold) GetThresholdsByItem(items []AggThresholdItem, itemID int) []AggThresholdItem { | ||||
|  | 	var filtered []AggThresholdItem | ||||
|  | 	for _, th := range items { | ||||
|  | 		if th.Item == itemID { | ||||
|  | 			filtered = append(filtered, th) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return filtered | ||||
|  | } | ||||
|  | 
 | ||||
|  | // GetThresholdsByTime 根据时间获取阈值
 | ||||
|  | func (t *AggThreshold) GetThresholdsByTime(date time.Time) []AggThresholdItem { | ||||
|  | 	if t.Items == nil || len(t.Items) == 0 { | ||||
|  | 		return nil | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	if !t.Items[0].IsTimeSegmented() { | ||||
|  | 		return t.Items | ||||
|  | 	} | ||||
|  | 
 | ||||
|  | 	hour := date.Hour() | ||||
|  | 	minute := date.Minute() | ||||
|  | 	sec := date.Second() | ||||
|  | 
 | ||||
|  | 	if hour == 0 && minute == 0 && sec == 0 { | ||||
|  | 		return t.filterByZero(t.Items, hour) | ||||
|  | 	} else if minute == 0 && sec == 0 { | ||||
|  | 		return t.filterByHour(t.Items, hour) | ||||
|  | 	} else { | ||||
|  | 		return t.filterByNormal(t.Items, hour) | ||||
|  | 	} | ||||
|  | } | ||||
|  | 
 | ||||
|  | // 0点时间的阈值模型
 | ||||
|  | func (t *AggThreshold) filterByZero(items []AggThresholdItem, hour int) []AggThresholdItem { | ||||
|  | 	var filteredItems []AggThresholdItem | ||||
|  | 	for _, item := range items { | ||||
|  | 		if item.Begin != nil && *item.Begin == hour { | ||||
|  | 			filteredItems = append(filteredItems, item) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return filteredItems | ||||
|  | } | ||||
|  | 
 | ||||
|  | // 整点时间的阈值模型
 | ||||
|  | func (t *AggThreshold) filterByHour(items []AggThresholdItem, hour int) []AggThresholdItem { | ||||
|  | 	var filteredItems []AggThresholdItem | ||||
|  | 	for _, item := range items { | ||||
|  | 		if item.Begin != nil && item.End != nil && *item.Begin < hour && *item.End >= hour { | ||||
|  | 			filteredItems = append(filteredItems, item) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return filteredItems | ||||
|  | } | ||||
|  | 
 | ||||
|  | // 常规时间的阈值模型
 | ||||
|  | func (t *AggThreshold) filterByNormal(items []AggThresholdItem, hour int) []AggThresholdItem { | ||||
|  | 	var filteredItems []AggThresholdItem | ||||
|  | 	for _, item := range items { | ||||
|  | 		if item.Begin != nil && item.End != nil && *item.Begin <= hour && *item.End > hour { | ||||
|  | 			filteredItems = append(filteredItems, item) | ||||
|  | 		} | ||||
|  | 	} | ||||
|  | 	return filteredItems | ||||
|  | } | ||||
								
									Binary file not shown.
								
							
						
					
					Loading…
					
					
				
		Reference in new issue