Browse Source

add 重建common_models

dev v0.0.7
lucas 4 months ago
parent
commit
7d82139626
  1. 56
      IotaData.go
  2. 9
      LICENSE
  3. 26
      README.md
  4. 54
      aggData.go
  5. 195
      alarm.go
  6. 40
      alarmMsg.go
  7. 208
      cacheData.go
  8. 6
      constant.go
  9. 21
      constant/iotaScheme/iotaScheme.go
  10. 11
      constant/logTag/logTag.go
  11. 101
      constant/redisKey/redisKey.go
  12. 13
      constant/settlementParam/settlementParam.go
  13. 14
      dataTrace.go
  14. 25
      dataUnit.go
  15. 107
      deviceData.go
  16. 23
      deviceFactorProto.go
  17. 8
      deviceInfo.go
  18. 71
      deviceMeta.go
  19. 17
      esGroupTheme.go
  20. 37
      esRaw.go
  21. 41
      esTheme.go
  22. 47
      esVbRaw.go
  23. 78
      factor.go
  24. 67
      filter.go
  25. 55
      formula.go
  26. 82
      formulaType.go
  27. 11
      go.mod
  28. 5
      go.sum
  29. 33
      iotaAlarm.go
  30. 76
      iotaDeploy.go
  31. 25
      iotaDevice.go
  32. 64
      iotaScheme.go
  33. 8
      processData.go
  34. 10
      rpc_node.go
  35. 146
      staionGroup.go
  36. 181
      station.go
  37. 179
      stationGroup_test.go
  38. 33
      structure.go
  39. 131
      threshold.go
  40. 132
      threshold_agg.go
  41. BIN
      新建 RTF 文件.rtf

56
IotaData.go

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

9
LICENSE

@ -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.

26
README.md

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

54
aggData.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 ""
}

195
alarm.go

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

40
alarmMsg.go

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

208
cacheData.go

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

6
constant.go

@ -0,0 +1,6 @@
package common_models
const (
RawTypeVib = "vib"
RawTypeDiag = "diag"
)

21
constant/iotaScheme/iotaScheme.go

@ -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"
)

11
constant/logTag/logTag.go

@ -0,0 +1,11 @@
package logTag
const (
Station = "s"
Device = "d"
Thing = "t"
Structure = "st"
Group = "group"
GroupItem = "group_item"
Formula = "formula"
)

101
constant/redisKey/redisKey.go

@ -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"
)

13
constant/settlementParam/settlementParam.go

@ -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"
)

14
dataTrace.go

@ -0,0 +1,14 @@
package common_models
import "time"
type DataTrace interface {
// 数据时间
_t() time.Time
// 定位标记
_q() string
// 定位标记
_r() string
}

25
dataUnit.go

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

107
deviceData.go

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

23
deviceFactorProto.go

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

8
deviceInfo.go

@ -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"`
}

71
deviceMeta.go

@ -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"`
}

17
esGroupTheme.go

@ -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"`
}

37
esRaw.go

@ -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"`
}

41
esTheme.go

@ -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"`
}

47
esVbRaw.go

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

78
factor.go

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

67
filter.go

@ -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"`
}

55
formula.go

@ -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"`
}

82
formulaType.go

@ -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
)

11
go.mod

@ -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
)

5
go.sum

@ -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=

33
iotaAlarm.go

@ -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"`
}

76
iotaDeploy.go

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

25
iotaDevice.go

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

64
iotaScheme.go

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

8
processData.go

@ -0,0 +1,8 @@
package common_models
type ProcessData struct {
//设备信息
DeviceData DeviceData
//测点信息
Stations []Station //设备可对应多个测点
}

10
rpc_node.go

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

146
staionGroup.go

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

181
station.go

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

179
stationGroup_test.go

@ -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("")
}

33
structure.go

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

131
threshold.go

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

132
threshold_agg.go

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

BIN
新建 RTF 文件.rtf

Binary file not shown.
Loading…
Cancel
Save