| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789 |
- package controllers
- import (
- "fmt"
- "os"
- "strconv"
- "strings"
- "time"
- "github.com/astaxie/beego"
- "lc/common/models"
- "lc/common/mqtt"
- "lc/common/protocol"
- )
- type GatewayController struct {
- BaseController
- }
- type ReqGateway struct {
- Code string `json:"code"` //设备编号,禁止修改
- Tenant string `json:"tenant"` //租户ID
- Name string `json:"name"` //设备名称
- Brand int `json:"brand"` //品牌
- Model int `json:"model"` //型号
- State int `json:"state"` //1启用,0禁用
- }
- type Gateway struct {
- Code string `json:"code"` //设备编号,禁止修改
- Name string `json:"name"` //设备名称
- Brand int `json:"brand"` //品牌
- Model int `json:"model"` //型号
- State int `json:"state"` //1启用,0禁用
- }
- type ReqImportGateway struct {
- Tenant string `json:"tenant"` //租户ID
- List []Gateway `json:"list"` //网关列表
- }
- type RespImport struct {
- Code string `json:"code"`
- Error string `json:"error"`
- }
- // CreateGateway @Title 创建网关设备
- // @Description 创建网关设备
- // @Param body controllers.ReqGateway true "数据"
- // @Success 0 {int} BaseResponse.Code "成功"
- // @Failure 1 {int} BaseResponse.Code "失败"
- // @router /v1/create [post]
- func (o *GatewayController) CreateGateway() {
- var obj ReqGateway
- if err := json.Unmarshal(o.Ctx.Input.RequestBody, &obj); err != nil {
- beego.Debug(string(o.Ctx.Input.RequestBody))
- o.Response(Failure, fmt.Sprintf("数据包解析错误:%s", err.Error()), nil)
- return
- }
- oo := models.Gateway{
- ID: obj.Code,
- Name: obj.Name,
- Tenant: obj.Tenant,
- Brand: obj.Brand,
- Model: obj.Model,
- State: obj.State,
- }
- if err := oo.SaveFromWeb(); err != nil {
- o.Response(Failure, fmt.Sprintf("数据插入失败:%s", err.Error()), nil)
- return
- }
- o.Response(Success, "成功", oo.ID)
- }
- // UpdateGateway @Title 更新网关设备
- // @Description 更新网关设备
- // @Param body controllers.ReqGateway true "数据"
- // @Success 0 {int} BaseResponse.Code "成功"
- // @Failure 1 {int} BaseResponse.Code "失败"
- // @router /v1/update [post]
- func (o *GatewayController) UpdateGateway() {
- var obj ReqGateway
- if err := json.Unmarshal(o.Ctx.Input.RequestBody, &obj); err != nil {
- beego.Debug(string(o.Ctx.Input.RequestBody))
- o.Response(Failure, fmt.Sprintf("数据包解析错误:%s", err.Error()), nil)
- return
- }
- oo := models.Gateway{
- ID: obj.Code,
- Name: obj.Name,
- Tenant: obj.Tenant,
- Brand: obj.Brand,
- Model: obj.Model,
- State: obj.State,
- }
- if err := oo.SaveFromWeb(); err != nil {
- o.Response(Failure, fmt.Sprintf("数据更新失败:%s", err.Error()), nil)
- return
- }
- o.Response(Success, "成功", oo.ID)
- }
- // DeleteGateway @Title 删除网关设备
- // @Description 删除网关设备
- // @Param code query string true "设备ID"
- // @Success 0 {int} BaseResponse.Code "成功"
- // @Failure 1 {int} BaseResponse.Code "失败"
- // @router /v1/delete [post]
- func (o *GatewayController) DeleteGateway() {
- code := strings.Trim(o.GetString("code"), " ")
- if len(code) == 0 {
- o.Response(Failure, "code为空", nil)
- return
- }
- c := models.Gateway{
- ID: code,
- }
- if err := c.Delete(); err != nil {
- beego.Error(fmt.Sprintf("删除失败:%s", err.Error()))
- o.Response(Failure, fmt.Sprintf("数据删除失败:%s", err.Error()), nil)
- return
- }
- o.Response(Success, "成功", c.ID)
- }
- // ImportGateway @Title 批量导入网关设备
- // @Description 批量导入网关设备
- // @Param body controllers.ReqImportGateway true "数据"
- // @Success 0 {int} BaseResponse.Code "成功"
- // @Failure 1 {int} BaseResponse.Code "失败"
- // @router /v1/import [post]
- func (o *GatewayController) ImportGateway() {
- var obj ReqImportGateway
- if err := json.Unmarshal(o.Ctx.Input.RequestBody, &obj); err != nil {
- beego.Debug(string(o.Ctx.Input.RequestBody))
- o.Response(Failure, fmt.Sprintf("数据包解析错误:%s", err.Error()), nil)
- return
- }
- var resp []RespImport
- for _, v := range obj.List {
- var aresp RespImport
- aresp.Code = v.Code
- oo := models.Gateway{
- ID: v.Code,
- Name: v.Name,
- Tenant: obj.Tenant,
- Brand: v.Brand,
- Model: v.Model,
- State: v.State,
- }
- err := oo.SaveFromWeb()
- if err != nil {
- aresp.Error = err.Error()
- beego.Error(fmt.Sprintf("zigbee集控器数据导入失败,code=%s,失败原因:%s", v.Code, err.Error()))
- }
- resp = append(resp, aresp)
- }
- 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)
- }
|