lucas
4 months ago
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