This commit is contained in:
tfl
2024-08-21 17:35:50 +08:00
parent 34ea7472b7
commit dd5b38b4e3
24 changed files with 328 additions and 133 deletions

View File

@@ -7,7 +7,6 @@ package api
// ========================================================================== // ==========================================================================
import ( import (
"fmt" "fmt"
"pandax/apps/device/util"
"pandax/kit/biz" "pandax/kit/biz"
"pandax/kit/model" "pandax/kit/model"
"pandax/kit/restfulx" "pandax/kit/restfulx"
@@ -131,6 +130,18 @@ func (p *DeviceApi) GetDeviceStatus(rc *restfulx.ReqCtx) {
rc.ResData = res rc.ResData = res
} }
func (p *DeviceApi) GetDeviceEvents(rc *restfulx.ReqCtx) {
pageNum := restfulx.QueryInt(rc, "pageNum", 1)
pageSize := restfulx.QueryInt(rc, "pageSize", 10)
deviceId := restfulx.PathParam(rc, "id")
ty := restfulx.QueryParam(rc, "type")
offset := pageSize * (pageNum - 1)
sql := `select * from ? where deviceId = ? and type = ? DESC LIMIT ?,? `
list, err := global.TdDb.GetAllEvents(sql, deviceId, ty, offset, pageSize)
biz.ErrIsNilAppendErr(err, "查询设备事件历史失败")
rc.ResData = list
}
// GetDeviceTelemetryHistory 获取Device属性的遥测历史 // GetDeviceTelemetryHistory 获取Device属性的遥测历史
func (p *DeviceApi) GetDeviceTelemetryHistory(rc *restfulx.ReqCtx) { func (p *DeviceApi) GetDeviceTelemetryHistory(rc *restfulx.ReqCtx) {
id := restfulx.PathParam(rc, "id") id := restfulx.PathParam(rc, "id")
@@ -146,21 +157,6 @@ func (p *DeviceApi) GetDeviceTelemetryHistory(rc *restfulx.ReqCtx) {
rc.ResData = rs rc.ResData = rs
} }
// 下发设备属性
func (p *DeviceApi) DownAttribute(rc *restfulx.ReqCtx) {
id := restfulx.PathParam(rc, "id")
key := restfulx.QueryParam(rc, "key")
value := restfulx.QueryParam(rc, "value")
biz.NotEmpty(value, "请设置属性值")
err := util.BuildRunDeviceRpc(id, "single", map[string]interface{}{
"method": "setAttributes",
"params": map[string]interface{}{
key: value,
},
})
biz.ErrIsNilAppendErr(err, "下发失败:")
}
// InsertDevice 添加Device // InsertDevice 添加Device
func (p *DeviceApi) InsertDevice(rc *restfulx.ReqCtx) { func (p *DeviceApi) InsertDevice(rc *restfulx.ReqCtx) {
var data entity.Device var data entity.Device

View File

@@ -3,17 +3,15 @@ package api
// ========================================================================== // ==========================================================================
import ( import (
"encoding/json" "encoding/json"
"pandax/apps/device/util"
"pandax/kit/biz" "pandax/kit/biz"
"pandax/kit/model" "pandax/kit/model"
"pandax/kit/restfulx" "pandax/kit/restfulx"
"pandax/kit/utils" devicerpc "pandax/pkg/device_rpc"
"pandax/pkg/global"
"strings" "strings"
"time"
"pandax/apps/device/entity" "pandax/apps/device/entity"
"pandax/apps/device/services" "pandax/apps/device/services"
"pandax/apps/device/util"
) )
type DeviceCmdLogApi struct { type DeviceCmdLogApi struct {
@@ -50,22 +48,12 @@ func (p *DeviceCmdLogApi) InsertDeviceCmdLog(rc *restfulx.ReqCtx) {
err := json.Unmarshal([]byte(data.CmdContent), &ms) err := json.Unmarshal([]byte(data.CmdContent), &ms)
biz.ErrIsNil(err, "指令格式不正确") biz.ErrIsNil(err, "指令格式不正确")
biz.IsTrue(len(ms) > 0, "指令格式不正确") biz.IsTrue(len(ms) > 0, "指令格式不正确")
data.Id = utils.GenerateID()
data.State = "2"
data.RequestTime = time.Now().Format("2006-01-02 15:04:05")
go func() { go func() {
err := util.BuildRunDeviceRpc(data.DeviceId, data.Mode, map[string]interface{}{ rpc := devicerpc.RpcPayload{
"method": data.CmdName, Method: data.CmdName,
"params": ms, Params: ms,
})
if err != nil {
global.Log.Error("规则链执行失败", err)
data.State = "1"
} else {
data.State = "0"
} }
data.ResponseTime = time.Now().Format("2006-01-02 15:04:05.000") err := util.BuildRunDeviceRpc(data.DeviceId, data.Mode, rpc)
err = p.DeviceCmdLogApp.Insert(data)
biz.ErrIsNil(err, "添加指令记录失败") biz.ErrIsNil(err, "添加指令记录失败")
}() }()
} }

View File

@@ -80,6 +80,18 @@ func InitDeviceRouter(container *restful.Container) {
Returns(200, "OK", []entity.DeviceStatusVo{}). Returns(200, "OK", []entity.DeviceStatusVo{}).
Returns(404, "Not Found", nil)) Returns(404, "Not Found", nil))
ws.Route(ws.GET("/{id}/event").To(func(request *restful.Request, response *restful.Response) {
restfulx.NewReqCtx(request, response).WithLog("获取设备事件历史").Handle(s.GetDeviceEvents)
}).
Doc("获取设备事件历史").
Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")).
Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")).
Param(ws.PathParameter("id", "设备ID").Required(true).DataType("string")).
Param(ws.QueryParameter("type", "事件类型").Required(true).DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags). // on the response
Returns(200, "OK", []map[string]any{}).
Returns(404, "Not Found", nil))
ws.Route(ws.GET("/{id}/property/history").To(func(request *restful.Request, response *restful.Response) { ws.Route(ws.GET("/{id}/property/history").To(func(request *restful.Request, response *restful.Response) {
restfulx.NewReqCtx(request, response).WithLog("获取设备属性的遥测历史").Handle(s.GetDeviceTelemetryHistory) restfulx.NewReqCtx(request, response).WithLog("获取设备属性的遥测历史").Handle(s.GetDeviceTelemetryHistory)
}). }).
@@ -93,15 +105,6 @@ func InitDeviceRouter(container *restful.Container) {
Returns(200, "OK", []map[string]any{}). Returns(200, "OK", []map[string]any{}).
Returns(404, "Not Found", nil)) Returns(404, "Not Found", nil))
ws.Route(ws.GET("/{id}/attribute/down").To(func(request *restful.Request, response *restful.Response) {
restfulx.NewReqCtx(request, response).WithLog("获取Device属性下发").Handle(s.DownAttribute)
}).
Doc("获取Device属性下发").
Param(ws.PathParameter("id", "Id").DataType("string")).
Param(ws.QueryParameter("key", "属性KEY").Required(false).DataType("string")).
Param(ws.QueryParameter("value", "属性Value").Required(false).DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("").To(func(request *restful.Request, response *restful.Response) { ws.Route(ws.POST("").To(func(request *restful.Request, response *restful.Response) {
restfulx.NewReqCtx(request, response).WithLog("添加Device信息").Handle(s.InsertDevice) restfulx.NewReqCtx(request, response).WithLog("添加Device信息").Handle(s.InsertDevice)
}). }).

View File

@@ -10,6 +10,7 @@ type (
ProductTemplateModel interface { ProductTemplateModel interface {
Insert(data entity.ProductTemplate) (*entity.ProductTemplate, error) Insert(data entity.ProductTemplate) (*entity.ProductTemplate, error)
FindOne(id string) (*entity.ProductTemplate, error) FindOne(id string) (*entity.ProductTemplate, error)
FindOneByKey(deviceId, key string) (*entity.ProductTemplate, error)
FindListPage(page, pageSize int, data entity.ProductTemplate) (*[]entity.ProductTemplate, int64, error) FindListPage(page, pageSize int, data entity.ProductTemplate) (*[]entity.ProductTemplate, int64, error)
FindListAttrs(data entity.ProductTemplate) (*[]entity.ProductTemplate, error) FindListAttrs(data entity.ProductTemplate) (*[]entity.ProductTemplate, error)
FindList(data entity.ProductTemplate) (*[]entity.ProductTemplate, error) FindList(data entity.ProductTemplate) (*[]entity.ProductTemplate, error)
@@ -38,6 +39,13 @@ func (m *templateModelImpl) FindOne(id string) (*entity.ProductTemplate, error)
return resData, err return resData, err
} }
func (m *templateModelImpl) FindOneByKey(deviceId, key string) (*entity.ProductTemplate, error) {
resData := new(entity.ProductTemplate)
db := global.Db.Table(m.table).Where("pid = ?", deviceId).Where("key = ?", key)
err := db.First(resData).Error
return resData, err
}
func (m *templateModelImpl) FindListPage(page, pageSize int, data entity.ProductTemplate) (*[]entity.ProductTemplate, int64, error) { func (m *templateModelImpl) FindListPage(page, pageSize int, data entity.ProductTemplate) (*[]entity.ProductTemplate, int64, error) {
list := make([]entity.ProductTemplate, 0) list := make([]entity.ProductTemplate, 0)
var total int64 = 0 var total int64 = 0

View File

@@ -7,13 +7,15 @@ import (
"pandax/apps/device/services" "pandax/apps/device/services"
ruleEntity "pandax/apps/rule/entity" ruleEntity "pandax/apps/rule/entity"
ruleService "pandax/apps/rule/services" ruleService "pandax/apps/rule/services"
"pandax/pkg/cache"
devicerpc "pandax/pkg/device_rpc"
"pandax/pkg/global" "pandax/pkg/global"
"pandax/pkg/rule_engine" "pandax/pkg/rule_engine"
"pandax/pkg/rule_engine/message" "pandax/pkg/rule_engine/message"
"pandax/pkg/tool" "pandax/pkg/tool"
) )
func BuildRunDeviceRpc(deviceId, mode string, msgData map[string]interface{}) error { func BuildRunDeviceRpc(deviceId, mode string, rp devicerpc.RpcPayload) error {
device, err := services.DeviceModelDao.FindOne(deviceId) device, err := services.DeviceModelDao.FindOne(deviceId)
if err != nil { if err != nil {
return err return err
@@ -35,10 +37,21 @@ func BuildRunDeviceRpc(deviceId, mode string, msgData map[string]interface{}) er
dataCode := ruleData.LfData.DataCode dataCode := ruleData.LfData.DataCode
code, _ := json.Marshal(dataCode) code, _ := json.Marshal(dataCode)
//新建规则链实体 //新建规则链实体
instance, errs := rule_engine.NewRuleChainInstance(findOne.Id, code) instance := &rule_engine.RuleChainInstance{}
ruleInstance, bo := cache.GetProductRule(device.Product.Id)
if !bo {
instance, err = rule_engine.NewRuleChainInstance(findOne.Id, code)
if err != nil { if err != nil {
return errs return err
} }
} else {
if data, ok := ruleInstance.(*rule_engine.RuleChainInstance); ok {
instance = data
} else {
return errors.New("规则实体解析错误")
}
}
metadataVals := map[string]interface{}{ metadataVals := map[string]interface{}{
"deviceId": device.Id, "deviceId": device.Id,
"mode": mode, "mode": mode,
@@ -49,10 +62,11 @@ func BuildRunDeviceRpc(deviceId, mode string, msgData map[string]interface{}) er
"orgId": device.OrgId, "orgId": device.OrgId,
"owner": device.Owner, "owner": device.Owner,
} }
msg := message.NewMessage(device.Owner, message.RpcRequestToDevice, msgData, metadataVals)
msg := message.NewMessage(device.Owner, message.RpcRequestToDevice, rp.ToMap(), metadataVals)
err = instance.StartRuleChain(context.Background(), msg) err = instance.StartRuleChain(context.Background(), msg)
if err != nil { if err != nil {
global.Log.Error("规则链执行失败", errs) global.Log.Error("规则链执行失败", err)
} }
return err return err
} }

View File

@@ -91,7 +91,7 @@ devA 为设备标识
"devB": "offline" "devB": "offline"
} }
``` ```
## 命令下发,设备请求格式, ## 服务端命令下发,设备请求格式,
```json ```json
{ {
"method": "restart", "method": "restart",
@@ -103,14 +103,28 @@ devA 为设备标识
} }
} }
``` ```
属性下发 method: 'setAttributes' ## 服务端属性下发 method: 'setAttributes'
## 命令响应的格式
```json ```json
{ {
"method": "2343", "method": "setAttributes",
"params": { "params": {
"aa": "2" "aa": "2"
} }
} }
``` ```
## 设备端 请求的格式
{
"method": "getCurrentTime",
"params": {
"aa": "2"
}
}
## 设备端 响应的格式
{
"method": "cmdResp",
"params": {
"aa": "2"
}
}

View File

@@ -3,13 +3,11 @@ package mqttclient
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/rand"
"time"
) )
const ( const (
RpcRespTopic = `v1/devices/me/rpc/response/%d` RpcRespTopic = `v1/devices/me/rpc/response/%s`
RpcReqTopic = `v1/devices/me/rpc/request/%d` RpcReqTopic = `v1/devices/me/rpc/request/%s`
) )
const ( const (
@@ -18,7 +16,7 @@ const (
) )
type RpcRequest struct { type RpcRequest struct {
RequestId int RequestId string
Mode string //单向、双向 单项只发送不等待响应 双向需要等到响应 Mode string //单向、双向 单项只发送不等待响应 双向需要等到响应
Timeout int // 设置双向时,等待的超时时间 Timeout int // 设置双向时,等待的超时时间
} }
@@ -41,9 +39,3 @@ func (rpc RpcRequest) Pub(deviceId, reqPayload string) error {
} }
return Publish(topic, value.(string), reqPayload) return Publish(topic, value.(string), reqPayload)
} }
func (rpc *RpcRequest) GetRequestId() {
rand.Seed(time.Now().UnixNano())
// 生成随机整数
rpc.RequestId = rand.Intn(10000) + 1 // 生成0到99之间的随机整数
}

View File

@@ -0,0 +1,47 @@
package udpclient
import (
"encoding/hex"
"net"
"pandax/pkg/global"
"sync"
)
type UdpClientT struct {
Conn *net.UDPConn
Addr *net.UDPAddr
}
var UdpClient sync.Map
func Send(deviceId, msg string) error {
if conn, ok := UdpClient.Load(deviceId); ok {
conn := conn.(*UdpClientT)
global.Log.Infof("设备%s, 发送指令%s", deviceId, msg)
_, err := conn.Conn.WriteToUDP([]byte(msg), conn.Addr)
if err != nil {
return err
}
} else {
global.Log.Infof("设备%s TCP连接不存在, 发送指令失败", deviceId)
}
return nil
}
func SendHex(deviceId, msg string) error {
if conn, ok := UdpClient.Load(deviceId); ok {
conn := conn.(*UdpClientT)
global.Log.Infof("设备%s, 发送指令%s", deviceId, msg)
b, err := hex.DecodeString(msg)
if err != nil {
return err
}
_, err = conn.Conn.WriteToUDP(b, conn.Addr)
if err != nil {
return err
}
} else {
global.Log.Infof("设备%s TCP连接不存在, 发送指令失败", deviceId)
}
return nil
}

View File

@@ -14,8 +14,12 @@ import (
"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/message"
"pandax/pkg/tdengine"
"pandax/pkg/tool" "pandax/pkg/tool"
"pandax/pkg/websocket" "pandax/pkg/websocket"
"time"
"github.com/kakuilan/kgo"
) )
// 消息处理模块 // 消息处理模块
@@ -38,11 +42,8 @@ func (s *HookService) handleOne(msg *netbase.DeviceEventInfo) {
<-s.Ch <-s.Ch
} }
}() }()
// 去除上传数据的非法空字符
// msg.Datas = strings.ReplaceAll(msg.Datas, "\\u0000", "")
switch msg.Type { switch msg.Type {
case message.RowMes, message.AttributesMes, message.TelemetryMes, message.RpcRequestFromDevice: case message.RowMes, message.AttributesMes, message.TelemetryMes, message.RpcRequestFromDevice, message.UpEventMes:
msgVals := make(map[string]interface{}) msgVals := make(map[string]interface{})
err := json.Unmarshal([]byte(msg.Datas), &msgVals) err := json.Unmarshal([]byte(msg.Datas), &msgVals)
if err != nil { if err != nil {
@@ -65,6 +66,29 @@ func (s *HookService) handleOne(msg *netbase.DeviceEventInfo) {
global.Log.Error("规则链执行失败", err) global.Log.Error("规则链执行失败", err)
return return
} }
// 保存事件信息
if msg.Type == message.UpEventMes {
tsl, err := services.ProductTemplateModelDao.FindOneByKey(msg.DeviceId, msg.Identifier)
if err != nil {
return
}
ci := &tdengine.Events{
DeviceId: msg.DeviceId,
Name: msg.Identifier,
Type: tsl.Type,
Content: msg.Datas,
Ts: time.Now().Format("2006-01-02 15:04:05.000"),
}
data, err := kgo.KConv.Struct2Map(ci, "")
if err != nil {
global.Log.Error("事件格式转化错误")
return
}
err = global.TdDb.InsertEvent(data)
if err != nil {
global.Log.Error("事件添加错误", err)
}
}
case message.DisConnectMes, message.ConnectMes: case message.DisConnectMes, message.ConnectMes:
// 更改设备在线状态 // 更改设备在线状态
if msg.Type == message.ConnectMes { if msg.Type == message.ConnectMes {

View File

@@ -2,7 +2,6 @@ package netbase
import ( import (
"encoding/json" "encoding/json"
"fmt"
"pandax/apps/device/entity" "pandax/apps/device/entity"
"pandax/apps/device/services" "pandax/apps/device/services"
exhook "pandax/iothub/server/emqxserver/protobuf" exhook "pandax/iothub/server/emqxserver/protobuf"
@@ -182,6 +181,15 @@ func SplitLwm2mClientID(lwm2mClientID string, index int) string {
} }
func GetRequestIdFromTopic(reg, topic string) (requestId string) { func GetRequestIdFromTopic(reg, topic string) (requestId string) {
re := regexp.MustCompile(reg)
res := re.FindStringSubmatch(topic)
if len(res) > 2 {
return res[2]
}
return ""
}
func GetEventFromTopic(reg, topic string) (identifier string) {
re := regexp.MustCompile(reg) re := regexp.MustCompile(reg)
res := re.FindStringSubmatch(topic) res := re.FindStringSubmatch(topic)
if len(res) > 1 { if len(res) > 1 {
@@ -190,13 +198,14 @@ func GetRequestIdFromTopic(reg, topic string) (requestId string) {
return "" return ""
} }
func CreateConnectionInfo(msgType, protocol, clientID, peerHost string, deviceAuth *model.DeviceAuth) *DeviceEventInfo { // eventType 事件类型 info alarm
func CreateEvent(msgType, eventType, content string, deviceAuth *model.DeviceAuth) *DeviceEventInfo {
ts := time.Now().Format("2006-01-02 15:04:05.000") ts := time.Now().Format("2006-01-02 15:04:05.000")
ci := &tdengine.Events{ ci := &tdengine.Events{
DeviceId: deviceAuth.DeviceId, DeviceId: deviceAuth.DeviceId,
Name: msgType, Name: msgType,
Type: "info", Type: eventType,
Content: fmt.Sprintf("设备%s, %s 事件", deviceAuth.Name, msgType), Content: content,
Ts: ts, Ts: ts,
} }
v, err := json.Marshal(*ci) v, err := json.Marshal(*ci)

View File

@@ -10,7 +10,8 @@ type DeviceEventInfo struct {
DeviceAuth *model.DeviceAuth `json:"deviceAuth"` DeviceAuth *model.DeviceAuth `json:"deviceAuth"`
Datas string `json:"datas"` Datas string `json:"datas"`
Type string `json:"type"` Type string `json:"type"`
RequestId string `json:"requestId"` RequestId string `json:"requestId"` // rpc 请求ID
Identifier string `json:"identifier"` //事件标识
} }
func (j *DeviceEventInfo) Bytes() []byte { func (j *DeviceEventInfo) Bytes() []byte {

View File

@@ -16,7 +16,9 @@ const (
TelemetryGatewayTopic = "v1/gateway/telemetry" TelemetryGatewayTopic = "v1/gateway/telemetry"
ConnectGatewayTopic = "v1/gateway/connect" ConnectGatewayTopic = "v1/gateway/connect"
RpcReq = `v1/devices/me/rpc/request/(.*?)$` RpcReq = `v1/devices/me/rpc/(.*?)/(.*?)$`
EventReq = `v1/devices/event/(.*?)$`
) )
var IotHubTopic = NewIotHubTopic() var IotHubTopic = NewIotHubTopic()
@@ -39,8 +41,11 @@ func (iht TopicMeg) GetMessageType(topic string) string {
if meg, ok := iht[topic]; ok { if meg, ok := iht[topic]; ok {
return meg return meg
} }
if strings.Contains(topic, "v1/devices/me/rpc/request") { if strings.Contains(topic, "v1/devices/me/rpc/request") || strings.Contains(topic, "v1/devices/me/rpc/response") {
return message.RpcRequestFromDevice return message.RpcRequestFromDevice
} }
if strings.Contains(topic, "v1/devices/event") {
return message.UpEventMes
}
return "" return ""
} }

View File

@@ -88,7 +88,7 @@ func (s *HookGrpcService) OnClientConnected(ctx context.Context, in *exhook2.Cli
} }
//添加连接ID //添加连接ID
mqttclient.Session.Store(etoken.DeviceId, in.Clientinfo.Clientid) mqttclient.Session.Store(etoken.DeviceId, in.Clientinfo.Clientid)
data := netbase.CreateConnectionInfo(message.ConnectMes, "mqtt", in.Clientinfo.Clientid, in.Clientinfo.Peerhost, etoken) data := netbase.CreateEvent(message.ConnectMes, "info", fmt.Sprintf("设备%s通过MQTT协议连接", etoken.Name), etoken)
go s.HookService.Queue.Queue(data) go s.HookService.Queue.Queue(data)
return &exhook2.EmptySuccess{}, nil return &exhook2.EmptySuccess{}, nil
} }
@@ -103,7 +103,7 @@ func (s *HookGrpcService) OnClientDisconnected(ctx context.Context, in *exhook2.
} }
//删除连接ID //删除连接ID
mqttclient.Session.Delete(etoken.DeviceId) mqttclient.Session.Delete(etoken.DeviceId)
data := netbase.CreateConnectionInfo(message.DisConnectMes, "mqtt", in.Clientinfo.Clientid, in.Clientinfo.Peerhost, etoken) data := netbase.CreateEvent(message.DisConnectMes, "info", fmt.Sprintf("设备%s断开连接", etoken.Name), etoken)
go s.HookService.Queue.Queue(data) go s.HookService.Queue.Queue(data)
return &exhook2.EmptySuccess{}, nil return &exhook2.EmptySuccess{}, nil
} }
@@ -243,10 +243,10 @@ func (s *HookGrpcService) OnMessagePublish(ctx context.Context, in *exhook2.Mess
if in.Message.Topic == ConnectGatewayTopic { if in.Message.Topic == ConnectGatewayTopic {
if val, ok := value.(string); ok { if val, ok := value.(string); ok {
if val == "online" { if val == "online" {
data = netbase.CreateConnectionInfo(message.ConnectMes, "mqtt", in.Message.From, in.Message.Headers["peerhost"], auth) data = netbase.CreateEvent(message.ConnectMes, "info", fmt.Sprintf("子设备%s通过网关连接", etoken.Name), auth)
} }
if val == "offline" { if val == "offline" {
data = netbase.CreateConnectionInfo(message.DisConnectMes, "mqtt", in.Message.From, in.Message.Headers["peerhost"], auth) data = netbase.CreateEvent(message.ConnectMes, "info", fmt.Sprintf("子设备设备%s通过网关连接", etoken.Name), auth)
} }
// 子设备发送到队列里 // 子设备发送到队列里
go s.HookService.Queue.Queue(data) go s.HookService.Queue.Queue(data)
@@ -281,6 +281,10 @@ func (s *HookGrpcService) OnMessagePublish(ctx context.Context, in *exhook2.Mess
// 获取请求id // 获取请求id
id := netbase.GetRequestIdFromTopic(RpcReq, in.Message.Topic) id := netbase.GetRequestIdFromTopic(RpcReq, in.Message.Topic)
data.RequestId = id data.RequestId = id
case message.UpEventMes:
data.Type = message.UpEventMes
identifier := netbase.GetEventFromTopic(EventReq, in.Message.Topic)
data.Identifier = identifier
} }
//将数据放到队列中 //将数据放到队列中
go s.HookService.Queue.Queue(data) go s.HookService.Queue.Queue(data)

View File

@@ -153,12 +153,12 @@ func (hhs *HookHttpService) hook(req *restful.Request, resp *restful.Response) {
func (cm *ConnectionManager) AddConnection(addr string, etoken *model.DeviceAuth, service *hook_message_work.HookService) { func (cm *ConnectionManager) AddConnection(addr string, etoken *model.DeviceAuth, service *hook_message_work.HookService) {
cm.activeConnections.Store(addr, etoken) cm.activeConnections.Store(addr, etoken)
data := netbase.CreateConnectionInfo(message.ConnectMes, "http", addr, addr, etoken) data := netbase.CreateEvent(message.ConnectMes, "info", fmt.Sprintf("设备%s通过HTTP协议连接", etoken.Name), etoken)
go service.Queue.Queue(data) go service.Queue.Queue(data)
} }
func (cm *ConnectionManager) RemoveConnection(addr string, etoken *model.DeviceAuth, service *hook_message_work.HookService) { func (cm *ConnectionManager) RemoveConnection(addr string, etoken *model.DeviceAuth, service *hook_message_work.HookService) {
data := netbase.CreateConnectionInfo(message.DisConnectMes, "http", addr, addr, etoken) data := netbase.CreateEvent(message.DisConnectMes, "info", fmt.Sprintf("设备%s断开连接", etoken.Name), etoken)
cm.activeConnections.Delete(addr) cm.activeConnections.Delete(addr)
go service.Queue.Queue(data) go service.Queue.Queue(data)
} }

View File

@@ -47,7 +47,7 @@ func handleConnection(conn *net.TCPConn, hs *hook_message_work.HookService) {
defer func() { defer func() {
_ = conn.Close() _ = conn.Close()
if isAuth { if isAuth {
data := netbase.CreateConnectionInfo(message.DisConnectMes, "tcp", conn.RemoteAddr().String(), conn.RemoteAddr().String(), etoken) data := netbase.CreateEvent(message.DisConnectMes, "info", fmt.Sprintf("设备%s断开连接", etoken.Name), etoken)
go hs.Queue.Queue(data) go hs.Queue.Queue(data)
} }
tcpclient.TcpClient.Delete(etoken.DeviceId) tcpclient.TcpClient.Delete(etoken.DeviceId)
@@ -67,7 +67,7 @@ func handleConnection(conn *net.TCPConn, hs *hook_message_work.HookService) {
auth := netbase.Auth(token) auth := netbase.Auth(token)
if auth { if auth {
global.Log.Infof("TCP协议 设备%s,认证成功", etoken.DeviceId) global.Log.Infof("TCP协议 设备%s,认证成功", etoken.DeviceId)
data := netbase.CreateConnectionInfo(message.ConnectMes, "tcp", conn.RemoteAddr().String(), conn.RemoteAddr().String(), etoken) data := netbase.CreateEvent(message.ConnectMes, "info", fmt.Sprintf("设备%s通过TCP协议连接", etoken.Name), etoken)
go hs.Queue.Queue(data) go hs.Queue.Queue(data)
isAuth = true isAuth = true
tcpclient.TcpClient.Store(etoken.DeviceId, conn) tcpclient.TcpClient.Store(etoken.DeviceId, conn)

View File

@@ -7,7 +7,7 @@ import (
"net" "net"
"time" "time"
udpclient "pandax/iothub/client/updclient" udpclient "pandax/iothub/client/udpclient"
"pandax/iothub/hook_message_work" "pandax/iothub/hook_message_work"
"pandax/iothub/netbase" "pandax/iothub/netbase"
"pandax/pkg/global" "pandax/pkg/global"
@@ -44,7 +44,7 @@ func InitUdpHook(addr string, hs *hook_message_work.HookService) {
_ = server.listener.Close() _ = server.listener.Close()
if isAuth, ok := authMap[client.AddrPort().String()]; ok && isAuth { if isAuth, ok := authMap[client.AddrPort().String()]; ok && isAuth {
data := netbase.CreateConnectionInfo(message.DisConnectMes, "udp", client.IP.String(), client.AddrPort().String(), etoken) data := netbase.CreateEvent(message.DisConnectMes, "info", fmt.Sprintf("设备%s断开连接", etoken.Name), etoken)
go hhs.HookService.Queue.Queue(data) go hhs.HookService.Queue.Queue(data)
} }
udpclient.UdpClient.Delete(etoken.DeviceId) udpclient.UdpClient.Delete(etoken.DeviceId)
@@ -72,7 +72,7 @@ func InitUdpHook(addr string, hs *hook_message_work.HookService) {
if auth { if auth {
global.Log.Infof("UDP协议 设备%s,认证成功", etoken.DeviceId) global.Log.Infof("UDP协议 设备%s,认证成功", etoken.DeviceId)
data := netbase.CreateConnectionInfo(message.ConnectMes, "udp", client.IP.String(), client.AddrPort().String(), etoken) data := netbase.CreateEvent(message.ConnectMes, "info", fmt.Sprintf("设备%s通过UDP协议连接", etoken.Name), etoken)
go hhs.HookService.Queue.Queue(data) go hhs.HookService.Queue.Queue(data)
authMap[client.AddrPort().String()] = true authMap[client.AddrPort().String()] = true

View File

@@ -11,6 +11,10 @@ func ComputeIfAbsentProductRule(key string, fun func(any) (any, error)) (any, er
return ProductCache.ComputeIfAbsent(key, fun) return ProductCache.ComputeIfAbsent(key, fun)
} }
func GetProductRule(key string) (any, bool) {
return ProductCache.Get(key)
}
func DelProductRule(key string) { func DelProductRule(key string) {
ProductCache.Delete(key) ProductCache.Delete(key)
} }

35
pkg/device_rpc/rpc.go Normal file
View File

@@ -0,0 +1,35 @@
package devicerpc
import (
"errors"
"fmt"
"time"
"github.com/kakuilan/kgo"
)
type RpcPayload struct {
Method string `json:"method"`
Params any `json:"params"`
}
func (rp *RpcPayload) ToMap() map[string]any {
data, err := kgo.KConv.Struct2Map(rp, "")
if err != nil {
return nil
}
return data
}
// GetRequestResult 处理设备端请求服务端方法
func (rpc RpcPayload) GetRequestResult() (string, error) {
//TODO 此处处理设备的请求参数逻辑
//自己定义请求逻辑
if rpc.Params == "getCurrentTime" {
unix := time.Now().Unix()
msg := fmt.Sprintf("%d", unix)
return msg, nil
}
// 获取属性 ...
return "", errors.New("未获取到请求方法")
}

View File

@@ -19,17 +19,19 @@ type RuleChainInstance struct {
} }
func NewRuleChainInstance(ruleID string, data []byte) (*RuleChainInstance, error) { func NewRuleChainInstance(ruleID string, data []byte) (*RuleChainInstance, error) {
instance := &RuleChainInstance{}
manifest, err := manifest.New(data) manifest, err := manifest.New(data)
if err != nil { if err != nil {
logrus.WithError(err).Errorf("invalid manifest file") logrus.WithError(err).Errorf("invalid manifest file")
return nil, err return nil, err
} }
withManifest, err := newInstanceWithManifest(manifest) instance, err = newInstanceWithManifest(manifest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
withManifest.ruleID = ruleID instance.ruleID = ruleID
return withManifest, nil
return instance, nil
} }
func newInstanceWithManifest(m *manifest.Manifest) (*RuleChainInstance, error) { func newInstanceWithManifest(m *manifest.Manifest) (*RuleChainInstance, error) {

View File

@@ -2,16 +2,22 @@ package nodes
import ( import (
"errors" "errors"
"pandax/apps/device/services"
"pandax/iothub/client/mqttclient" "pandax/iothub/client/mqttclient"
"pandax/iothub/client/tcpclient" "pandax/iothub/client/tcpclient"
"pandax/iothub/client/udpclient"
"pandax/kit/utils"
devicerpc "pandax/pkg/device_rpc"
"pandax/pkg/global" "pandax/pkg/global"
"pandax/pkg/global/model"
"pandax/pkg/rule_engine/message" "pandax/pkg/rule_engine/message"
"time"
"github.com/kakuilan/kgo"
) )
type rpcRequestFromDeviceNode struct { type rpcRequestFromDeviceNode struct {
bareNode bareNode
RequestId int `json:"requestId"` RequestId string `json:"requestId"`
} }
type rpcRequestFromDeviceFactory struct{} type rpcRequestFromDeviceFactory struct{}
@@ -34,10 +40,18 @@ func (n *rpcRequestFromDeviceNode) Handle(msg *message.Message) error {
if msg.Msg.GetValue("method") == nil || msg.Msg.GetValue("params") == nil { if msg.Msg.GetValue("method") == nil || msg.Msg.GetValue("params") == nil {
return errors.New("指令请求格式错误") return errors.New("指令请求格式错误")
} }
var rpcp = model.RpcPayload{
var rpcp = devicerpc.RpcPayload{
Method: msg.Msg.GetValue("method").(string), Method: msg.Msg.GetValue("method").(string),
Params: msg.Msg.GetValue("params"), Params: msg.Msg.GetValue("params"),
} }
var err error
// 指令下发响应
if rpcp.Method == "cmdResp" {
if requestId, ok := msg.Metadata.GetValue("requestId").(string); ok {
services.DeviceCmdLogModelDao.UpdateResp(requestId, kgo.KConv.ToStr(rpcp.Params), time.Now().Format("2006-01-02 15:04:05"))
}
} else {
result, err := rpcp.GetRequestResult() result, err := rpcp.GetRequestResult()
if err != nil { if err != nil {
if failureLableNode != nil { if failureLableNode != nil {
@@ -53,14 +67,14 @@ func (n *rpcRequestFromDeviceNode) Handle(msg *message.Message) error {
deviceProtocol = msg.Metadata.GetValue("deviceProtocol").(string) deviceProtocol = msg.Metadata.GetValue("deviceProtocol").(string)
} }
deviceId := msg.Metadata.GetValue("deviceId").(string) deviceId := msg.Metadata.GetValue("deviceId").(string)
if deviceProtocol == global.MQTTProtocol { if deviceProtocol == global.MQTTProtocol || deviceProtocol == global.CoAPProtocol || deviceProtocol == global.LwM2MProtocol {
rpc := &mqttclient.RpcRequest{} rpc := &mqttclient.RpcRequest{}
RequestId := n.RequestId RequestId := n.RequestId
if RequestId == 0 { if RequestId == "" {
if msg.Metadata.GetValue("requestId") == nil { if msg.Metadata.GetValue("requestId") == nil {
rpc.GetRequestId() rpc.RequestId = utils.GenerateID()
} else { } else {
RequestId = int(msg.Metadata.GetValue("requestId").(float64)) rpc.RequestId = msg.Metadata.GetValue("requestId").(string)
} }
} else { } else {
rpc.RequestId = RequestId rpc.RequestId = RequestId
@@ -70,6 +84,11 @@ func (n *rpcRequestFromDeviceNode) Handle(msg *message.Message) error {
if deviceProtocol == global.TCPProtocol { if deviceProtocol == global.TCPProtocol {
err = tcpclient.Send(deviceId, result) err = tcpclient.Send(deviceId, result)
} }
if deviceProtocol == global.UDPProtocol {
err = udpclient.Send(deviceId, result)
}
}
if err != nil { if err != nil {
n.Debug(msg, message.DEBUGOUT, err.Error()) n.Debug(msg, message.DEBUGOUT, err.Error())
if failureLableNode != nil { if failureLableNode != nil {

View File

@@ -3,11 +3,18 @@ package nodes
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"pandax/apps/device/entity"
"pandax/apps/device/services"
"pandax/iothub/client/mqttclient" "pandax/iothub/client/mqttclient"
"pandax/iothub/client/tcpclient" "pandax/iothub/client/tcpclient"
"pandax/iothub/client/udpclient"
"pandax/kit/utils"
"pandax/pkg/global" "pandax/pkg/global"
"pandax/pkg/global/model" "pandax/pkg/global/model"
"pandax/pkg/rule_engine/message" "pandax/pkg/rule_engine/message"
"time"
"github.com/kakuilan/kgo"
) )
type rpcRequestToDeviceNode struct { type rpcRequestToDeviceNode struct {
@@ -34,11 +41,24 @@ func (n *rpcRequestToDeviceNode) Handle(msg *message.Message) error {
if msg.Msg.GetValue("method") == nil || msg.Msg.GetValue("params") == nil { if msg.Msg.GetValue("method") == nil || msg.Msg.GetValue("params") == nil {
return errors.New("指令下发格式错误") return errors.New("指令下发格式错误")
} }
deviceId := msg.Metadata.GetValue("deviceId").(string)
// 创建请求格式
var datas = model.RpcPayload{ var datas = model.RpcPayload{
Method: msg.Msg.GetValue("method").(string), Method: msg.Msg.GetValue("method").(string),
Params: msg.Msg.GetValue("params"), Params: msg.Msg.GetValue("params"),
} }
payload, _ := json.Marshal(datas) payload, _ := json.Marshal(datas)
// 构建指令记录
var data entity.DeviceCmdLog
data.Id = utils.GenerateID()
data.DeviceId = deviceId
data.CmdName = datas.Method
data.CmdContent = kgo.KConv.ToStr(datas.Params)
data.Mode = msg.Metadata.GetValue("mode").(string)
data.State = "2"
data.RequestTime = time.Now().Format("2006-01-02 15:04:05")
mode := mqttclient.SingleMode mode := mqttclient.SingleMode
if n.Timeout > 0 { if n.Timeout > 0 {
mode = mqttclient.DoubleMode mode = mqttclient.DoubleMode
@@ -49,16 +69,20 @@ func (n *rpcRequestToDeviceNode) Handle(msg *message.Message) error {
deviceProtocol = msg.Metadata.GetValue("deviceProtocol").(string) deviceProtocol = msg.Metadata.GetValue("deviceProtocol").(string)
} }
var err error var err error
deviceId := msg.Metadata.GetValue("deviceId").(string) if deviceProtocol == global.MQTTProtocol || deviceProtocol == global.CoAPProtocol || deviceProtocol == global.LwM2MProtocol {
if deviceProtocol == global.MQTTProtocol { var rpc = &mqttclient.RpcRequest{Mode: mode, Timeout: n.Timeout, RequestId: data.Id}
var rpc = &mqttclient.RpcRequest{Mode: mode, Timeout: n.Timeout}
rpc.GetRequestId()
err = rpc.RequestCmd(deviceId, string(payload)) err = rpc.RequestCmd(deviceId, string(payload))
} }
if deviceProtocol == global.TCPProtocol { if deviceProtocol == global.TCPProtocol {
err = tcpclient.Send(deviceId, string(payload)) err = tcpclient.Send(deviceId, string(payload))
} }
if deviceProtocol == global.UDPProtocol {
err = udpclient.Send(deviceId, string(payload))
}
if err != nil { if err != nil {
data.State = "1"
services.DeviceCmdLogModelDao.Insert(data)
n.Debug(msg, message.DEBUGOUT, err.Error()) n.Debug(msg, message.DEBUGOUT, err.Error())
if failureLableNode != nil { if failureLableNode != nil {
return failureLableNode.Handle(msg) return failureLableNode.Handle(msg)
@@ -66,7 +90,10 @@ func (n *rpcRequestToDeviceNode) Handle(msg *message.Message) error {
return err return err
} }
} }
if successLableNode != nil { if successLableNode != nil {
data.State = "0"
services.DeviceCmdLogModelDao.Insert(data)
n.Debug(msg, message.DEBUGOUT, "") n.Debug(msg, message.DEBUGOUT, "")
return successLableNode.Handle(msg) return successLableNode.Handle(msg)
} }

View File

@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
) )
const connectTableName = "events" const connectTableName = "device_events"
type Events struct { type Events struct {
Ts string `json:"ts"` Ts string `json:"ts"`

View File

@@ -7,7 +7,7 @@ import (
"github.com/kakuilan/kgo" "github.com/kakuilan/kgo"
) )
const logTableName = "logs" const logTableName = "device_logs"
// 日志 TDengine // 日志 TDengine
type TdLog struct { type TdLog struct {
@@ -39,7 +39,7 @@ func (s *TdEngine) InsertLog(log *TdLog) (err error) {
func (s *TdEngine) ClearLog() (err error) { func (s *TdEngine) ClearLog() (err error) {
ts := time.Now().Add(-7 * 24 * time.Hour).Format("2006-01-02") ts := time.Now().Add(-7 * 24 * time.Hour).Format("2006-01-02")
sql := fmt.Sprintf("DELETE FROM %s WHERE ts < ?", logTableName) sql := fmt.Sprintf("DELETE FROM %s.%s WHERE ts < ?", s.dbName, logTableName)
_, err = s.db.Exec(sql, ts) _, err = s.db.Exec(sql, ts)
return return

View File

@@ -3,12 +3,14 @@ package websocket
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/gorilla/websocket"
"net/http" "net/http"
"pandax/apps/device/entity" "pandax/apps/device/entity"
"pandax/apps/device/util" "pandax/apps/device/util"
devicerpc "pandax/pkg/device_rpc"
"pandax/pkg/global" "pandax/pkg/global"
"strings" "strings"
"github.com/gorilla/websocket"
) )
var upGrader = websocket.Upgrader{ var upGrader = websocket.Upgrader{
@@ -65,10 +67,11 @@ func OnMessage(ws *Websocket, msg string) {
return return
} }
//2. 根据设备下发属性更改 //2. 根据设备下发属性更改
err = util.BuildRunDeviceRpc(vtsa.TwinId, "single", map[string]interface{}{ rpc := devicerpc.RpcPayload{
"method": "setAttributes", Method: "setAttributes",
"params": vtsa.Attrs, Params: vtsa.Attrs,
}) }
err = util.BuildRunDeviceRpc(vtsa.TwinId, "single", rpc)
if err != nil { if err != nil {
global.Log.Error("命令发送失败", err) global.Log.Error("命令发送失败", err)
sendMessages("02", "命令发送失败", screenId) sendMessages("02", "命令发送失败", screenId)