mirror of
https://gitee.com/XM-GO/PandaX.git
synced 2026-05-07 04:21:25 +08:00
[feat]添加规则引擎debug功能
This commit is contained in:
@@ -11,7 +11,7 @@ type (
|
|||||||
DeviceAlarmModel interface {
|
DeviceAlarmModel interface {
|
||||||
Insert(data entity.DeviceAlarm) error
|
Insert(data entity.DeviceAlarm) error
|
||||||
FindOne(id string) *entity.DeviceAlarm
|
FindOne(id string) *entity.DeviceAlarm
|
||||||
FindOneByType(deviceId, ty, state string) *entity.DeviceAlarm
|
FindOneByType(deviceId, ty, state string) (*entity.DeviceAlarm, error)
|
||||||
FindListPage(page, pageSize int, data entity.DeviceAlarmForm) (*[]entity.DeviceAlarm, int64)
|
FindListPage(page, pageSize int, data entity.DeviceAlarmForm) (*[]entity.DeviceAlarm, int64)
|
||||||
Update(data entity.DeviceAlarm) error
|
Update(data entity.DeviceAlarm) error
|
||||||
Delete(ids []string)
|
Delete(ids []string)
|
||||||
@@ -41,12 +41,14 @@ func (m *alarmModelImpl) FindOne(id string) *entity.DeviceAlarm {
|
|||||||
return resData
|
return resData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *alarmModelImpl) FindOneByType(deviceId, ty, state string) *entity.DeviceAlarm {
|
func (m *alarmModelImpl) FindOneByType(deviceId, ty, state string) (*entity.DeviceAlarm, error) {
|
||||||
resData := new(entity.DeviceAlarm)
|
resData := new(entity.DeviceAlarm)
|
||||||
db := global.Db.Table(m.table).Where("device_id = ?", deviceId).Where("type = ? ", ty).Where("state = ? ", state)
|
db := global.Db.Table(m.table).Where("device_id = ?", deviceId).Where("type = ? ", ty).Where("state = ? ", state)
|
||||||
err := db.First(resData).Error
|
err := db.First(resData).Error
|
||||||
biz.ErrIsNil(err, "查询设备告警失败")
|
if err != nil {
|
||||||
return resData
|
return nil, err
|
||||||
|
}
|
||||||
|
return resData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *alarmModelImpl) FindListPage(page, pageSize int, data entity.DeviceAlarmForm) (*[]entity.DeviceAlarm, int64) {
|
func (m *alarmModelImpl) FindListPage(page, pageSize int, data entity.DeviceAlarmForm) (*[]entity.DeviceAlarm, int64) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func BuildRunDeviceRpc(deviceId, mode string, metadata map[string]interface{}) e
|
|||||||
dataCode := ruleData.LfData.DataCode
|
dataCode := ruleData.LfData.DataCode
|
||||||
code, _ := json.Marshal(dataCode)
|
code, _ := json.Marshal(dataCode)
|
||||||
//新建规则链实体
|
//新建规则链实体
|
||||||
instance, errs := rule_engine.NewRuleChainInstance(code)
|
instance, errs := rule_engine.NewRuleChainInstance(findOne.Id, code)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return errs[0]
|
return errs[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"github.com/PandaXGO/PandaKit/biz"
|
"github.com/PandaXGO/PandaKit/biz"
|
||||||
"github.com/PandaXGO/PandaKit/model"
|
"github.com/PandaXGO/PandaKit/model"
|
||||||
"github.com/PandaXGO/PandaKit/restfulx"
|
"github.com/PandaXGO/PandaKit/restfulx"
|
||||||
@@ -9,8 +8,6 @@ import (
|
|||||||
"pandax/apps/rule/services"
|
"pandax/apps/rule/services"
|
||||||
"pandax/pkg/global_model"
|
"pandax/pkg/global_model"
|
||||||
"pandax/pkg/rule_engine"
|
"pandax/pkg/rule_engine"
|
||||||
"pandax/pkg/rule_engine/message"
|
|
||||||
"pandax/pkg/rule_engine/nodes"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,14 +16,22 @@ type RuleChainApi struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleChainApi) GetNodeLabels(rc *restfulx.ReqCtx) {
|
func (r *RuleChainApi) GetNodeLabels(rc *restfulx.ReqCtx) {
|
||||||
rc.ResData = nodes.GetCategory()
|
rc.ResData = rule_engine.GetCategory()
|
||||||
}
|
}
|
||||||
func (r *RuleChainApi) RuleChainTest(rc *restfulx.ReqCtx) {
|
|
||||||
code := restfulx.QueryParam(rc, "code")
|
func (r *RuleChainApi) GetNodeDebug(rc *restfulx.ReqCtx) {
|
||||||
instance, _ := rule_engine.NewRuleChainInstance([]byte(code))
|
pageNum := restfulx.QueryInt(rc, "pageNum", 1)
|
||||||
msg := message.NewMessage("1", message.TelemetryMes, message.Msg{"temperature": 60.4, "humidity": 32.5}, message.Metadata{})
|
pageSize := restfulx.QueryInt(rc, "pageSize", 10)
|
||||||
instance.StartRuleChain(context.Background(), msg)
|
ruleId := restfulx.QueryParam(rc, "ruleId")
|
||||||
rc.ResData = []map[string]interface{}{}
|
nodeId := restfulx.QueryParam(rc, "nodeId")
|
||||||
|
|
||||||
|
total, list := rule_engine.GetDebugDataPage(pageNum, pageSize, ruleId, nodeId)
|
||||||
|
rc.ResData = model.ResultPage{
|
||||||
|
Total: total,
|
||||||
|
PageNum: int64(pageNum),
|
||||||
|
PageSize: int64(pageSize),
|
||||||
|
Data: list,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRuleChainList WorkInfo列表数据
|
// GetRuleChainList WorkInfo列表数据
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type RuleDataJson struct {
|
type RuleDataJson struct {
|
||||||
LfData struct {
|
Id string
|
||||||
|
LfData LfData `json:"lfData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LfData struct {
|
||||||
GlobalColor string `json:"globalColor"`
|
GlobalColor string `json:"globalColor"`
|
||||||
DataCode map[string]interface{} `json:"dataCode"`
|
DataCode map[string]interface{} `json:"dataCode"`
|
||||||
OpenRule bool `json:"openRule"`
|
OpenRule bool `json:"openRule"`
|
||||||
Setting map[string]interface{} `json:"setting"`
|
Setting map[string]interface{} `json:"setting"`
|
||||||
} `json:"lfData"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 序列化
|
// 序列化
|
||||||
|
|||||||
@@ -27,12 +27,17 @@ func InitRuleChainRouter(container *restful.Container) {
|
|||||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||||
Returns(200, "OK", model.ResultPage{}))
|
Returns(200, "OK", model.ResultPage{}))
|
||||||
|
|
||||||
ws.Route(ws.GET("/test").To(func(request *restful.Request, response *restful.Response) {
|
ws.Route(ws.GET("/node/debug").To(func(request *restful.Request, response *restful.Response) {
|
||||||
restfulx.NewReqCtx(request, response).WithNeedCasbin(false).WithLog("测试规则引擎").Handle(s.RuleChainTest)
|
restfulx.NewReqCtx(request, response).WithNeedCasbin(false).WithLog("获取规则链节点日志").Handle(s.GetNodeDebug)
|
||||||
}).
|
}).
|
||||||
Doc("测试规则引擎").
|
Doc("获取规则链节点日志").
|
||||||
Param(ws.QueryParameter("code", "流程代码").DataType("string")).
|
Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")).
|
||||||
Metadata(restfulspec.KeyOpenAPITags, tags))
|
Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")).
|
||||||
|
Param(ws.QueryParameter("ruleId", "规则ID").Required(false).DataType("string")).
|
||||||
|
Param(ws.QueryParameter("nodeId", "节点ID").Required(false).DataType("string")).
|
||||||
|
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||||
|
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||||
|
Returns(200, "OK", model.ResultPage{}))
|
||||||
|
|
||||||
ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) {
|
ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) {
|
||||||
restfulx.NewReqCtx(request, response).WithLog("获取规则引擎分页列表").Handle(s.GetRuleChainList)
|
restfulx.NewReqCtx(request, response).WithLog("获取规则引擎分页列表").Handle(s.GetRuleChainList)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (s *HookService) handleOne(msg *netbase.DeviceEventInfo) {
|
|||||||
dataCode := chain.LfData.DataCode
|
dataCode := chain.LfData.DataCode
|
||||||
code, err := json.Marshal(dataCode)
|
code, err := json.Marshal(dataCode)
|
||||||
//新建规则链实体
|
//新建规则链实体
|
||||||
instance, errs := rule_engine.NewRuleChainInstance(code)
|
instance, errs := rule_engine.NewRuleChainInstance(chain.Id, code)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
global.Log.Error("规则链初始化失败", errs[0])
|
global.Log.Error("规则链初始化失败", errs[0])
|
||||||
return
|
return
|
||||||
@@ -116,13 +116,19 @@ func getRuleChain(etoken *global_model.DeviceAuth) *ruleEntity.RuleDataJson {
|
|||||||
get, err := cache.ComputeIfAbsentProductRule(key, func(k any) (any, error) {
|
get, err := cache.ComputeIfAbsentProductRule(key, func(k any) (any, error) {
|
||||||
one := services.ProductModelDao.FindOne(k.(string))
|
one := services.ProductModelDao.FindOne(k.(string))
|
||||||
rule := ruleService.RuleChainModelDao.FindOne(one.RuleChainId)
|
rule := ruleService.RuleChainModelDao.FindOne(one.RuleChainId)
|
||||||
return rule.RuleDataJson, nil
|
var lfData ruleEntity.LfData
|
||||||
|
err := tool.StringToStruct(rule.RuleDataJson, &lfData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ruleEntity.RuleDataJson{Id: rule.Id, LfData: lfData}, nil
|
||||||
})
|
})
|
||||||
biz.ErrIsNil(err, "缓存读取规则链失败")
|
biz.ErrIsNil(err, "缓存读取规则链失败")
|
||||||
ruleData := ruleEntity.RuleDataJson{}
|
if ruleData, ok := get.(ruleEntity.RuleDataJson); ok {
|
||||||
err = tool.StringToStruct(get.(string), &ruleData)
|
|
||||||
biz.ErrIsNil(err, "规则链数据转化失败")
|
|
||||||
return &ruleData
|
return &ruleData
|
||||||
|
}
|
||||||
|
biz.ErrIsNil(err, "规则链数据转化失败")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建规则链执行的消息
|
// 构建规则链执行的消息
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package initialize
|
|||||||
import (
|
import (
|
||||||
"pandax/apps/device/entity"
|
"pandax/apps/device/entity"
|
||||||
"pandax/apps/device/services"
|
"pandax/apps/device/services"
|
||||||
|
ruleEntity "pandax/apps/rule/entity"
|
||||||
"pandax/pkg/cache"
|
"pandax/pkg/cache"
|
||||||
"pandax/pkg/events"
|
"pandax/pkg/events"
|
||||||
"pandax/pkg/global"
|
"pandax/pkg/global"
|
||||||
|
"pandax/pkg/tool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 初始化事件监听
|
// 初始化事件监听
|
||||||
@@ -17,8 +19,10 @@ func InitEvents() {
|
|||||||
RuleChainId: ruleId,
|
RuleChainId: ruleId,
|
||||||
})
|
})
|
||||||
if list != nil {
|
if list != nil {
|
||||||
|
var lfData ruleEntity.LfData
|
||||||
|
tool.StringToStruct(codeData, &lfData)
|
||||||
for _, product := range *list {
|
for _, product := range *list {
|
||||||
cache.PutProductRule(product.Id, codeData)
|
cache.PutProductRule(product.Id, ruleEntity.RuleDataJson{Id: ruleId, LfData: lfData})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,14 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
TIME_TYPE_PROP = "telemetry"
|
|
||||||
TIME_TYPE_ATRE = "attributes"
|
|
||||||
TIME_TYPE_LOGS = "logs"
|
|
||||||
TIME_TYPE_ALARM = "alarm"
|
|
||||||
TIME_TYPE_EVENT = "event"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TdEngine struct {
|
type TdEngine struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
dbName string
|
dbName string
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package tdengine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kakuilan/kgo"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const connectTableName = "device_connect"
|
||||||
|
|
||||||
type ConnectInfo struct {
|
type ConnectInfo struct {
|
||||||
Ts string `json:"ts"`
|
Ts string `json:"ts"`
|
||||||
ClientID string `json:"clientId"`
|
ClientID string `json:"clientId"`
|
||||||
@@ -18,26 +18,12 @@ type ConnectInfo struct {
|
|||||||
|
|
||||||
// CreateEventTable 创建设备连接事件表
|
// CreateEventTable 创建设备连接事件表
|
||||||
func (s *TdEngine) CreateEventTable() (err error) {
|
func (s *TdEngine) CreateEventTable() (err error) {
|
||||||
sql := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.device_connect (ts TIMESTAMP,deviceId NCHAR(64),
|
sql := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.%s (ts TIMESTAMP,deviceId NCHAR(64),
|
||||||
type NCHAR(64),clientId NCHAR(64),peerHost NCHAR(64),sockPort NCHAR(64),protocol NCHAR(64))`, s.dbName)
|
type NCHAR(64),clientId NCHAR(64),peerHost NCHAR(64),sockPort NCHAR(64),protocol NCHAR(64))`, s.dbName, connectTableName)
|
||||||
_, err = s.db.Exec(sql)
|
_, err = s.db.Exec(sql)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TdEngine) InsertEvent(data map[string]any) (err error) {
|
func (s *TdEngine) InsertEvent(data map[string]any) (err error) {
|
||||||
if len(data) == 0 {
|
return s.InsertDevice(connectTableName, data)
|
||||||
return
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
field = []string{}
|
|
||||||
value = []string{}
|
|
||||||
)
|
|
||||||
for k, v := range data {
|
|
||||||
field = append(field, k)
|
|
||||||
value = append(value, "'"+kgo.KConv.ToStr(v)+"'")
|
|
||||||
}
|
|
||||||
|
|
||||||
sql := "INSERT INTO ? (?) VALUES (?)"
|
|
||||||
_, err = s.db.Exec(sql, "device_connect", strings.Join(field, ","), strings.Join(value, ","))
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package tdengine
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
const logTableName = "device_log"
|
||||||
|
|
||||||
// CreateLogStable 添加LOG超级表
|
// CreateLogStable 添加LOG超级表
|
||||||
func (s *TdEngine) CreateLogStable() (err error) {
|
func (s *TdEngine) CreateLogStable() (err error) {
|
||||||
var name string
|
var name string
|
||||||
|
|||||||
18
pkg/tdengine/tdengine_rule_debug.go
Normal file
18
pkg/tdengine/tdengine_rule_debug.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package tdengine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const debugTableName = "device_rule_debug"
|
||||||
|
|
||||||
|
func (s *TdEngine) CreateDeviceRuleDebugTable() (err error) {
|
||||||
|
sql := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.%s (ts TIMESTAMP,nodeId NCHAR(64),msgd NCHAR(64),debugType NCHAR(64),
|
||||||
|
deviceName NCHAR(64),msgType NCHAR(64),msg VARCHAR,metadata VARCHAR,error VARCHAR)`, s.dbName, debugTableName)
|
||||||
|
_, err = s.db.Exec(sql)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TdEngine) InsertRuleDebug(data map[string]any) (err error) {
|
||||||
|
return s.InsertDevice(debugTableName, data)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package tool
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"pandax/pkg/global"
|
"pandax/pkg/global"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -67,6 +68,21 @@ func FirstLowCamelString(s string) string {
|
|||||||
return string(data)
|
return string(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StructToMap(s interface{}) map[string]interface{} {
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
|
value := reflect.ValueOf(s)
|
||||||
|
typ := reflect.TypeOf(s)
|
||||||
|
|
||||||
|
for i := 0; i < value.NumField(); i++ {
|
||||||
|
field := typ.Field(i)
|
||||||
|
fieldValue := value.Field(i).Interface()
|
||||||
|
result[field.Name] = fieldValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func MapToStruct(m map[string]interface{}, s interface{}) error {
|
func MapToStruct(m map[string]interface{}, s interface{}) error {
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
2047
resource/pandax_iot_pg.sql
Normal file
2047
resource/pandax_iot_pg.sql
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user