|
|
@@ -2,11 +2,16 @@ package controllers
|
|
|
|
|
|
import (
|
|
|
"fmt"
|
|
|
+ "os"
|
|
|
+ "strconv"
|
|
|
"strings"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/astaxie/beego"
|
|
|
|
|
|
"lc/common/models"
|
|
|
+ "lc/common/mqtt"
|
|
|
+ "lc/common/protocol"
|
|
|
)
|
|
|
|
|
|
type GatewayController struct {
|
|
|
@@ -151,3 +156,634 @@ func (o *GatewayController) ImportGateway() {
|
|
|
}
|
|
|
o.Response(Success, "成功", resp)
|
|
|
}
|
|
|
+
|
|
|
+// PushSerialConfig @Title 下发串口配置到网关
|
|
|
+// @Description 下发 serial.json 配置到指定网关
|
|
|
+// @Param body controllers.ReqGatewayConfig true "数据"
|
|
|
+// @Success 0 {int} BaseResponse.Code "成功"
|
|
|
+// @Failure 1 {int} BaseResponse.Code "失败"
|
|
|
+// @router /v1/serial/push [post]
|
|
|
+func (o *GatewayController) PushSerialConfig() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ if gid == "" || tenant == "" {
|
|
|
+ o.Response(Failure, "gid和tenant不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ body := o.Ctx.Input.RequestBody
|
|
|
+ if len(body) == 0 {
|
|
|
+ o.Response(Failure, "请求body不能为空,请在表单中导入或添加串口配置后再下发", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ sc := protocol.SerialConfig{Serial: make(map[uint8]*protocol.SerialPort)}
|
|
|
+ if err := json.Unmarshal(body, &sc); err != nil || len(sc.Serial) == 0 {
|
|
|
+ o.Response(Failure, "串口配置JSON解析失败或为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ content := string(body)
|
|
|
+
|
|
|
+ seq := GetNextUint64()
|
|
|
+ var obj protocol.Pack_SeqFileObject
|
|
|
+ obj.Data.File = "serial.json"
|
|
|
+ obj.Data.Content = content
|
|
|
+ str, err := obj.EnCode(gid, seq)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("编码失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ topic := GetTopic(tenant, protocol.DT_GATEWAY, gid, protocol.TP_GW_SET_SERIAL)
|
|
|
+ if err := GetMqttHandler().PublishString(topic, str, mqtt.AtLeastOnce); err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("MQTT发布失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下发成功后,将配置存档到DB
|
|
|
+ pushedCodes := make(map[int]bool)
|
|
|
+ for _, v := range sc.Serial {
|
|
|
+ models.G_db.Save(&models.GatewaySerial{
|
|
|
+ ID: gid, ComID: int(v.Code),
|
|
|
+ Interface: v.Interface, Address: v.Address,
|
|
|
+ BaudRate: v.BaudRate, DataBits: int(v.DataBits),
|
|
|
+ StopBits: int(v.StopBits), Parity: v.Parity,
|
|
|
+ Timeout: int(v.Timeout), ProtocolType: int(v.ProtocolType),
|
|
|
+ })
|
|
|
+ pushedCodes[int(v.Code)] = true
|
|
|
+ }
|
|
|
+ // 标记不在此次推送中的旧串口为删除
|
|
|
+ var oldSerials []models.GatewaySerial
|
|
|
+ models.G_db.Where("id = ?", gid).Find(&oldSerials)
|
|
|
+ for _, s := range oldSerials {
|
|
|
+ if !pushedCodes[s.ComID] {
|
|
|
+ models.G_db.Delete(&s)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录指令
|
|
|
+ dcr := models.DeviceCmdRecord{ID: seq, GID: gid, DID: gid, Topic: topic, Message: str, State: 0}
|
|
|
+ if err := models.G_db.Create(&dcr).Error; err != nil {
|
|
|
+ beego.Error("PushSerialConfig:指令入库失败:", err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ o.Response(Success, "串口配置已下发并保存", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// PushDevConfig @Title 下发设备配置到网关
|
|
|
+// @Description 下发 dev/{code}.json 配置到指定网关
|
|
|
+// @Param body controllers.ReqDevConfig true "数据"
|
|
|
+// @Success 0 {int} BaseResponse.Code "成功"
|
|
|
+// @Failure 1 {int} BaseResponse.Code "失败"
|
|
|
+// @router /v1/dev/push [post]
|
|
|
+func (o *GatewayController) PushDevConfig() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ codeStr := strings.Trim(o.GetString("code"), " ")
|
|
|
+ if gid == "" || tenant == "" || codeStr == "" {
|
|
|
+ o.Response(Failure, "gid、tenant、code不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ code, err := strconv.Atoi(codeStr)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("code格式错误:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var mdc protocol.MapDevConfig
|
|
|
+ // 优先从请求body解析(前端表单直接提交的配置)
|
|
|
+ body := o.Ctx.Input.RequestBody
|
|
|
+ if len(body) == 0 {
|
|
|
+ o.Response(Failure, "请求body不能为空,请在表单中导入或添加设备配置后再下发", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err := json.Unmarshal(body, &mdc); err != nil || len(mdc.Rtu) == 0 {
|
|
|
+ o.Response(Failure, "设备配置JSON解析失败或为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ content := string(body)
|
|
|
+
|
|
|
+ seq := GetNextUint64()
|
|
|
+ var obj protocol.Pack_SeqFileObject
|
|
|
+ obj.Data.File = codeStr + ".json"
|
|
|
+ obj.Data.Content = content
|
|
|
+ str, err := obj.EnCode(gid, seq)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("编码失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ topic := GetTopic(tenant, protocol.DT_GATEWAY, gid, protocol.TP_GW_SET_RTU)
|
|
|
+ if err := GetMqttHandler().PublishString(topic, str, mqtt.AtLeastOnce); err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("MQTT发布失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下发成功后,将配置存档到DB
|
|
|
+ pushedDevCodes := make(map[string]bool)
|
|
|
+ for _, v := range mdc.Rtu {
|
|
|
+ models.G_db.Save(&models.GatewayDevice{
|
|
|
+ ID: v.DevCode, Name: v.Name, GID: gid,
|
|
|
+ ComID: int(v.Code), RtuID: int(v.DevID), TID: int(v.TID),
|
|
|
+ SendCloud: v.SendCloud, WaitTime: int(v.WaitTime),
|
|
|
+ ProtocolType: int(v.ProtocolType), DevType: int(v.DevType),
|
|
|
+ Tenant: tenant, State: 1,
|
|
|
+ })
|
|
|
+ pushedDevCodes[v.DevCode] = true
|
|
|
+ }
|
|
|
+ // 标记不在此次推送中且同串口下的旧设备为删除
|
|
|
+ var oldDevices []models.GatewayDevice
|
|
|
+ models.G_db.Where("g_id = ? AND com_id = ? AND state = 1", gid, code).Find(&oldDevices)
|
|
|
+ for _, d := range oldDevices {
|
|
|
+ if !pushedDevCodes[d.ID] {
|
|
|
+ models.G_db.Model(&d).Update("state", 0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录指令
|
|
|
+ dcr := models.DeviceCmdRecord{ID: seq, GID: gid, DID: gid, Topic: topic, Message: str, State: 0}
|
|
|
+ if err := models.G_db.Create(&dcr).Error; err != nil {
|
|
|
+ beego.Error("PushDevConfig:指令入库失败:", err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ o.Response(Success, "设备配置已下发并保存", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// PushModelConfig @Title 下发物模型配置到网关
|
|
|
+// @Description 下发 model/{tid}.json 配置到指定网关
|
|
|
+// @Param body controllers.ReqModelConfig true "数据"
|
|
|
+// @Success 0 {int} BaseResponse.Code "成功"
|
|
|
+// @Failure 1 {int} BaseResponse.Code "失败"
|
|
|
+// @router /v1/model/push [post]
|
|
|
+func (o *GatewayController) PushModelConfig() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ tidStr := strings.Trim(o.GetString("tid"), " ")
|
|
|
+ if gid == "" || tenant == "" || tidStr == "" {
|
|
|
+ o.Response(Failure, "gid、tenant、tid不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ tid, err := strconv.Atoi(tidStr)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("tid格式错误:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 必须从请求body取模型JSON(不下发时以表单数据为准)
|
|
|
+ body := o.Ctx.Input.RequestBody
|
|
|
+ if len(body) == 0 {
|
|
|
+ o.Response(Failure, "请求body不能为空,请在表单中选择JSON文件后再下发", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ fileContent := string(body)
|
|
|
+ var iot protocol.IotModel
|
|
|
+ if err := json.Unmarshal(body, &iot); err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("物模型JSON解析失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ device := iot.Device
|
|
|
+ modelName := iot.Model
|
|
|
+ protocolName := iot.Protocol
|
|
|
+
|
|
|
+ seq := GetNextUint64()
|
|
|
+ var obj protocol.Pack_SeqFileObject
|
|
|
+ obj.Data.File = tidStr + ".json"
|
|
|
+ obj.Data.Content = fileContent
|
|
|
+ str, err := obj.EnCode(gid, seq)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("编码失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ topic := GetTopic(tenant, protocol.DT_GATEWAY, gid, protocol.TP_GW_SET_MODEL)
|
|
|
+ if err := GetMqttHandler().PublishString(topic, str, mqtt.AtLeastOnce); err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("MQTT发布失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录指令
|
|
|
+ dcr := models.DeviceCmdRecord{ID: seq, GID: gid, DID: gid, Topic: topic, Message: str, State: 0}
|
|
|
+ if err := models.G_db.Create(&dcr).Error; err != nil {
|
|
|
+ beego.Error("PushModelConfig:指令入库失败:", err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下发成功后存档到 t_gateway_model
|
|
|
+ models.G_db.Save(&models.GatewayModel{
|
|
|
+ GID: gid, TID: uint16(tid),
|
|
|
+ Device: device, Model: modelName, Protocol: protocolName,
|
|
|
+ File: fileContent, PushedAt: time.Now(),
|
|
|
+ })
|
|
|
+
|
|
|
+ o.Response(Success, "物模型配置已下发并保存", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// SerialConfigList @Title 查询网关串口列表
|
|
|
+// @Description 查询指定网关的所有串口配置
|
|
|
+// @Param gid query string true "网关ID"
|
|
|
+// @Success 0 {int} BaseResponse.Code "成功"
|
|
|
+// @router /v1/serial/list [get]
|
|
|
+func (o *GatewayController) SerialConfigList() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ if gid == "" {
|
|
|
+ o.Response(Failure, "gid不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ var serials []models.GatewaySerial
|
|
|
+ if err := models.G_db.Where("id = ?", gid).Find(&serials).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("查询失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.Response(Success, "成功", serials)
|
|
|
+}
|
|
|
+
|
|
|
+// SerialConfigSave @Title 保存串口配置
|
|
|
+// @Description 新增或更新单条串口配置
|
|
|
+// @Param gid query string true "网关ID"
|
|
|
+// @Param comid query int true "串口编号"
|
|
|
+// @Success 0 {int} BaseResponse.Code "成功"
|
|
|
+// @router /v1/serial/save [post]
|
|
|
+func (o *GatewayController) SerialConfigSave() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ comIDStr := strings.Trim(o.GetString("comid"), " ")
|
|
|
+ if gid == "" || comIDStr == "" {
|
|
|
+ o.Response(Failure, "gid和comid不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ comID, err := strconv.Atoi(comIDStr)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("comid格式错误:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ baudRate, _ := strconv.Atoi(strings.Trim(o.GetString("baudrate"), " "))
|
|
|
+ dataBits, _ := strconv.Atoi(strings.Trim(o.GetString("databits"), " "))
|
|
|
+ stopBits, _ := strconv.Atoi(strings.Trim(o.GetString("stopbits"), " "))
|
|
|
+ timeout, _ := strconv.Atoi(strings.Trim(o.GetString("timeout"), " "))
|
|
|
+ protocolType, _ := strconv.Atoi(strings.Trim(o.GetString("protocoltype"), " "))
|
|
|
+
|
|
|
+ s := models.GatewaySerial{
|
|
|
+ ID: gid,
|
|
|
+ ComID: comID,
|
|
|
+ Interface: strings.Trim(o.GetString("interface"), " "),
|
|
|
+ Address: strings.Trim(o.GetString("address"), " "),
|
|
|
+ BaudRate: baudRate,
|
|
|
+ DataBits: dataBits,
|
|
|
+ StopBits: stopBits,
|
|
|
+ Parity: strings.Trim(o.GetString("parity"), " "),
|
|
|
+ Timeout: timeout,
|
|
|
+ ProtocolType: protocolType,
|
|
|
+ }
|
|
|
+ if err := models.G_db.Save(&s).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("保存失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.Response(Success, "保存成功", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// SerialConfigDelete @Title 删除串口配置
|
|
|
+// @Description 删除单条串口配置
|
|
|
+// @Param gid query string true "网关ID"
|
|
|
+// @Param comid query int true "串口编号"
|
|
|
+// @router /v1/serial/delete [post]
|
|
|
+func (o *GatewayController) SerialConfigDelete() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ comIDStr := strings.Trim(o.GetString("comid"), " ")
|
|
|
+ if gid == "" || comIDStr == "" {
|
|
|
+ o.Response(Failure, "gid和comid不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ comID, err := strconv.Atoi(comIDStr)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("comid格式错误:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err := models.G_db.Where("id = ? AND com_id = ?", gid, comID).Delete(&models.GatewaySerial{}).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("删除失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.Response(Success, "删除成功", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// DevConfigList @Title 查询网关设备列表
|
|
|
+// @Description 查询指定网关某串口下的设备配置
|
|
|
+// @Param gid query string true "网关ID"
|
|
|
+// @Param code query int false "串口编号,0查全部"
|
|
|
+// @router /v1/dev/list [get]
|
|
|
+func (o *GatewayController) DevConfigList() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ if gid == "" {
|
|
|
+ o.Response(Failure, "gid不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ codeStr := strings.Trim(o.GetString("code"), " ")
|
|
|
+ var devices []models.GatewayDevice
|
|
|
+ db := models.G_db.Where("g_id = ? AND state = 1", gid)
|
|
|
+ if codeStr != "" {
|
|
|
+ code, _ := strconv.Atoi(codeStr)
|
|
|
+ db = db.Where("com_id = ?", code)
|
|
|
+ }
|
|
|
+ if err := db.Find(&devices).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("查询失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.Response(Success, "成功", devices)
|
|
|
+}
|
|
|
+
|
|
|
+// DevConfigSave @Title 保存设备配置
|
|
|
+// @Description 新增或更新单条设备配置
|
|
|
+// @router /v1/dev/save [post]
|
|
|
+func (o *GatewayController) DevConfigSave() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ devCode := strings.Trim(o.GetString("devcode"), " ")
|
|
|
+ if gid == "" || devCode == "" {
|
|
|
+ o.Response(Failure, "gid和devcode不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ comID, _ := strconv.Atoi(strings.Trim(o.GetString("comid"), " "))
|
|
|
+ rtuID, _ := strconv.Atoi(strings.Trim(o.GetString("rtuid"), " "))
|
|
|
+ tid, _ := strconv.Atoi(strings.Trim(o.GetString("tid"), " "))
|
|
|
+ protocolType, _ := strconv.Atoi(strings.Trim(o.GetString("protocoltype"), " "))
|
|
|
+ devType, _ := strconv.Atoi(strings.Trim(o.GetString("devtype"), " "))
|
|
|
+ sendCloud, _ := strconv.Atoi(strings.Trim(o.GetString("sendcloud"), " "))
|
|
|
+ waitTime, _ := strconv.Atoi(strings.Trim(o.GetString("waittime"), " "))
|
|
|
+
|
|
|
+ d := models.GatewayDevice{
|
|
|
+ ID: devCode,
|
|
|
+ Name: strings.Trim(o.GetString("name"), " "),
|
|
|
+ GID: gid,
|
|
|
+ ComID: comID,
|
|
|
+ RtuID: rtuID,
|
|
|
+ TID: tid,
|
|
|
+ SendCloud: sendCloud,
|
|
|
+ WaitTime: waitTime,
|
|
|
+ ProtocolType: protocolType,
|
|
|
+ DevType: devType,
|
|
|
+ Tenant: tenant,
|
|
|
+ State: 1,
|
|
|
+ }
|
|
|
+ if err := models.G_db.Save(&d).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("保存失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.Response(Success, "保存成功", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// DevConfigDelete @Title 删除设备配置
|
|
|
+// @Description 删除单条设备配置(软删除)
|
|
|
+// @Param devcode query string true "设备编码"
|
|
|
+// @router /v1/dev/delete [post]
|
|
|
+func (o *GatewayController) DevConfigDelete() {
|
|
|
+ devCode := strings.Trim(o.GetString("devcode"), " ")
|
|
|
+ if devCode == "" {
|
|
|
+ o.Response(Failure, "devcode不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err := models.G_db.Model(&models.GatewayDevice{}).Where("id = ?", devCode).Update("state", 0).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("删除失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.Response(Success, "删除成功", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// ModelList @Title 查询物模型列表
|
|
|
+// @Description 查询网关已下发的物模型(默认查所有,传gid查指定网关)
|
|
|
+// @router /v1/model/list [get]
|
|
|
+func (o *GatewayController) ModelList() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ type ModelSummary struct {
|
|
|
+ TID uint16 `json:"tid"`
|
|
|
+ Device string `json:"device"`
|
|
|
+ Model string `json:"model"`
|
|
|
+ Protocol string `json:"protocol"`
|
|
|
+ File string `json:"file,omitempty"`
|
|
|
+ }
|
|
|
+ var list []ModelSummary
|
|
|
+ if gid != "" {
|
|
|
+ var gms []models.GatewayModel
|
|
|
+ if err := models.G_db.Where("g_id = ?", gid).Find(&gms).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("查询失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for _, v := range gms {
|
|
|
+ list = append(list, ModelSummary{TID: v.TID, Device: v.Device, Model: v.Model, Protocol: v.Protocol, File: v.File})
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ var gms []models.GatewayModel
|
|
|
+ if err := models.G_db.Find(&gms).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("查询失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for _, v := range gms {
|
|
|
+ list = append(list, ModelSummary{TID: v.TID, Device: v.Device, Model: v.Model, Protocol: v.Protocol, File: v.File})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ o.Response(Success, "成功", list)
|
|
|
+}
|
|
|
+
|
|
|
+// ModelSave @Title 保存物模型
|
|
|
+// @Description 新增或更新物模型到 t_gateway_model
|
|
|
+// @router /v1/model/save [post]
|
|
|
+func (o *GatewayController) ModelSave() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tidStr := strings.Trim(o.GetString("tid"), " ")
|
|
|
+ if gid == "" || tidStr == "" {
|
|
|
+ o.Response(Failure, "gid和tid不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ tid, err := strconv.Atoi(tidStr)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("tid格式错误:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ device := strings.Trim(o.GetString("device"), " ")
|
|
|
+ modelName := strings.Trim(o.GetString("model"), " ")
|
|
|
+ protocol := strings.Trim(o.GetString("protocol"), " ")
|
|
|
+ fileContent := strings.Trim(o.GetString("file"), " ")
|
|
|
+ if fileContent == "" {
|
|
|
+ // 尝试从上传的请求body中读取文件内容
|
|
|
+ fileContent = string(o.Ctx.Input.RequestBody)
|
|
|
+ }
|
|
|
+ m := models.GatewayModel{
|
|
|
+ GID: gid, TID: uint16(tid),
|
|
|
+ Device: device, Model: modelName, Protocol: protocol,
|
|
|
+ File: fileContent, PushedAt: time.Now(),
|
|
|
+ }
|
|
|
+ if err := models.G_db.Save(&m).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("保存失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.Response(Success, "保存成功", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// ModelDelete @Title 删除物模型
|
|
|
+// @Description 从 t_gateway_model 删除指定物模型
|
|
|
+// @router /v1/model/delete [post]
|
|
|
+func (o *GatewayController) ModelDelete() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tidStr := strings.Trim(o.GetString("tid"), " ")
|
|
|
+ if gid == "" || tidStr == "" {
|
|
|
+ o.Response(Failure, "gid和tid不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ tid, err := strconv.Atoi(tidStr)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("tid格式错误:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err := models.G_db.Where("g_id = ? AND t_id = ?", gid, tid).Delete(&models.GatewayModel{}).Error; err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("删除失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ o.Response(Success, "删除成功", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// LogQuery @Title 查询边缘日志
|
|
|
+// @Description 触发边缘上传日志文件
|
|
|
+// @router /v1/log/query [post]
|
|
|
+func (o *GatewayController) LogQuery() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ if gid == "" || tenant == "" {
|
|
|
+ o.Response(Failure, "gid和tenant不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ seq := GetNextUint64()
|
|
|
+ var obj protocol.Pack_IDObject
|
|
|
+ str, err := obj.EnCode(gid, seq, 0)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("编码失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ topic := GetTopic(tenant, protocol.DT_GATEWAY, gid, protocol.TP_GW_LOG)
|
|
|
+ beego.Info("LogQuery:发布,topic=", topic, ",payload=", str)
|
|
|
+ if err := GetMqttHandler().PublishString(topic, str, mqtt.AtLeastOnce); err != nil {
|
|
|
+ beego.Error("LogQuery:MQTT发布失败,topic=", topic, ",err=", err)
|
|
|
+ o.Response(Failure, fmt.Sprintf("MQTT发布失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ beego.Info("LogQuery:发布成功,topic=", topic)
|
|
|
+ o.Response(Success, "日志查询指令已发送,请稍后刷新查看", map[string]interface{}{"seq": seq})
|
|
|
+}
|
|
|
+
|
|
|
+// LogList @Title 列出已保存的日志文件
|
|
|
+// @Description 列出网关已上传的日志文件
|
|
|
+// @router /v1/log/list [get]
|
|
|
+func (o *GatewayController) LogList() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ if gid == "" || tenant == "" {
|
|
|
+ o.Response(Failure, "gid和tenant不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ dir := "/opt/ipole_data/gateway_logs/" + tenant + "/" + gid + "/log/"
|
|
|
+ entries, err := os.ReadDir(dir)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, "该网关暂无日志文件,请先点击「查询日志」", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ type LogFile struct {
|
|
|
+ Name string `json:"name"`
|
|
|
+ Size int64 `json:"size"`
|
|
|
+ Time string `json:"time"`
|
|
|
+ }
|
|
|
+ var files []LogFile
|
|
|
+ for _, e := range entries {
|
|
|
+ if !e.IsDir() {
|
|
|
+ info, _ := e.Info()
|
|
|
+ f := LogFile{Name: e.Name(), Size: info.Size(), Time: info.ModTime().Format("2006-01-02 15:04:05")}
|
|
|
+ files = append(files, f)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ o.Response(Success, "成功", files)
|
|
|
+}
|
|
|
+
|
|
|
+// LogDelete @Title 删除边缘日志
|
|
|
+// @Description 远程删除边缘网关的所有日志文件
|
|
|
+// @Param gid query string true "网关ID"
|
|
|
+// @Param tenant query string true "租户ID"
|
|
|
+// @router /v1/log/delete [post]
|
|
|
+func (o *GatewayController) LogDelete() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ if gid == "" || tenant == "" {
|
|
|
+ o.Response(Failure, "gid和tenant不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ seq := GetNextUint64()
|
|
|
+ var obj protocol.Pack_IDObject
|
|
|
+ str, err := obj.EnCode(gid, seq, 0)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("编码失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ topic := GetTopic(tenant, protocol.DT_GATEWAY, gid, protocol.TP_GW_REMOVE_LOG)
|
|
|
+ beego.Info("LogDelete:发布,topic=", topic)
|
|
|
+ if err := GetMqttHandler().PublishString(topic, str, mqtt.AtLeastOnce); err != nil {
|
|
|
+ beego.Error("LogDelete:MQTT发布失败,topic=", topic, ",err=", err)
|
|
|
+ o.Response(Failure, fmt.Sprintf("MQTT发布失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ beego.Info("LogDelete:发布成功,topic=", topic)
|
|
|
+ o.Response(Success, "日志删除指令已发送", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// LogCfg @Title 设置边缘日志等级
|
|
|
+// @Description 云端下发日志等级配置到边缘网关
|
|
|
+// @Param gid query string true "网关ID"
|
|
|
+// @Param tenant query string true "租户ID"
|
|
|
+// @Success 0 {int} BaseResponse.Code "成功"
|
|
|
+// @Failure 1 {int} BaseResponse.Code "失败"
|
|
|
+// @router /v1/log/cfg [post]
|
|
|
+func (o *GatewayController) LogCfg() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ if gid == "" || tenant == "" {
|
|
|
+ o.Response(Failure, "gid和tenant不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ body := o.Ctx.Input.RequestBody
|
|
|
+ if len(body) == 0 {
|
|
|
+ o.Response(Failure, "请求body为空,请提供日志等级配置", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ topic := GetTopic(tenant, protocol.DT_GATEWAY, gid, protocol.TP_GW_LOG_CFG)
|
|
|
+ beego.Info("LogCfg:发布,topic=", topic)
|
|
|
+ if err := GetMqttHandler().PublishString(topic, string(body), mqtt.AtLeastOnce); err != nil {
|
|
|
+ beego.Error("LogCfg:MQTT发布失败,topic=", topic, ",err=", err)
|
|
|
+ o.Response(Failure, fmt.Sprintf("MQTT发布失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ beego.Info("LogCfg:发布成功,topic=", topic)
|
|
|
+ o.Response(Success, "日志等级配置已下发", nil)
|
|
|
+}
|
|
|
+
|
|
|
+// LogRead @Title 读取日志内容
|
|
|
+// @Description 读取指定日志文件内容
|
|
|
+// @router /v1/log/read [get]
|
|
|
+func (o *GatewayController) LogRead() {
|
|
|
+ gid := strings.Trim(o.GetString("gid"), " ")
|
|
|
+ tenant := strings.Trim(o.GetString("tenant"), " ")
|
|
|
+ fname := strings.Trim(o.GetString("file"), " ")
|
|
|
+ if gid == "" || tenant == "" || fname == "" {
|
|
|
+ o.Response(Failure, "gid、tenant、file不能为空", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 防止路径遍历
|
|
|
+ if strings.Contains(fname, "..") || strings.Contains(fname, "/") || strings.Contains(fname, "\\") {
|
|
|
+ o.Response(Failure, "非法文件名", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ path := "/opt/ipole_data/gateway_logs/" + tenant + "/" + gid + "/log/" + fname
|
|
|
+ buf, err := os.ReadFile(path)
|
|
|
+ if err != nil {
|
|
|
+ o.Response(Failure, fmt.Sprintf("读取失败:%s", err.Error()), nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 限制返回最近100KB,避免超大文件
|
|
|
+ content := string(buf)
|
|
|
+ if len(content) > 100*1024 {
|
|
|
+ content = content[len(content)-100*1024:]
|
|
|
+ }
|
|
|
+ o.Response(Success, "成功", content)
|
|
|
+}
|