feature: support query devicestatus,reposond devicestatus from up platform

This commit is contained in:
pg
2025-03-22 17:18:59 +08:00
committed by pggiroro
parent 6c8c44486c
commit 18e77cd594
4 changed files with 200 additions and 5 deletions
+28 -3
View File
@@ -213,6 +213,13 @@ func (d *Device) onMessage(req *sip.Request, tx sip.ServerTransaction, msg *gb28
}
}
}
case "DeviceStatus":
d.Status = DeviceOnlineStatus
d.Online = true
if d.plugin.DB != nil {
d.UpdateTime = time.Now()
d.plugin.DB.Save(d)
}
case "DeviceInfo":
// 主设备信息
d.Name = msg.DeviceName
@@ -280,9 +287,11 @@ func (d *Device) Go() (err error) {
var response *sip.Response
response, err = d.queryDeviceInfo()
if err != nil {
d.Error("deviceInfo", "err", err)
} else {
d.Debug("deviceInfo", "response", response.String())
d.Error("queryDeviceInfo", "err", err)
}
response, err = d.queryDeviceStatus()
if err != nil {
d.Error("queryDeviceStatus", "err", err)
}
response, err = d.catalog()
if err != nil {
@@ -363,6 +372,16 @@ func (d *Device) CreateRequest(Method sip.RequestMethod, Recipient any) *sip.Req
Address: sip.Uri{User: d.DeviceID, Host: d.HostAddress},
}
req.AppendHeader(&toHeader)
viaHeader := sip.ViaHeader{
ProtocolName: "SIP",
ProtocolVersion: "2.0",
Transport: "UDP",
Host: d.LocalIP,
Port: d.LocalPort,
Params: sip.HeaderParams(sip.NewParams()),
}
viaHeader.Params.Add("branch", sip.GenerateBranchN(10)).Add("rport", "")
req.AppendHeader(&viaHeader)
//req.AppendHeader(&d.contactHDR)
return req
}
@@ -388,6 +407,12 @@ func (d *Device) queryDeviceInfo() (*sip.Response, error) {
return d.send(request)
}
func (d *Device) queryDeviceStatus() (*sip.Response, error) {
request := d.CreateRequest(sip.MESSAGE, nil)
request.SetBody(gb28181.BuildDeviceStatusXML(d.SN, d.DeviceID, d.Charset))
return d.send(request)
}
func (d *Device) subscribePosition(interval int) (*sip.Response, error) {
request := d.CreateRequest(sip.SUBSCRIBE, nil)
request.AppendHeader(sip.NewHeader("Expires", "3600"))
+7 -2
View File
@@ -574,8 +574,13 @@ func (gb *GB28181ProPlugin) OnMessage(req *sip.Request, tx sip.ServerTransaction
// 如果既不是设备也不是平台,返回404
if d == nil && p == nil {
var response *sip.Response
gb.Error("OnMessage", "error", "device/platform not found", "id", id)
response := sip.NewResponseFromRequest(req, sip.StatusNotFound, "Not Found", nil)
if temp.CmdType == "Keepalive" {
response = sip.NewResponseFromRequest(req, sip.StatusUnauthorized, "需要重新注册", nil)
} else {
response = sip.NewResponseFromRequest(req, sip.StatusNotFound, "Not Found", nil)
}
if err := tx.Respond(response); err != nil {
gb.Error("respond NotFound", "error", err.Error())
}
@@ -719,7 +724,7 @@ func (gb *GB28181ProPlugin) StoreDevice(deviceid string, req *sip.Request) (d *D
RegisterTime: now,
KeepaliveTime: now,
Status: DeviceRegisterStatus,
Online: true,
Online: false,
StreamMode: "UDP", // 默认UDP传输
Charset: "GB2312", // 默认GB2312字符集
GeoCoordSys: "WGS84", // 默认WGS84坐标系
+13
View File
@@ -42,6 +42,14 @@ const (
<SN>%d</SN>
<DeviceID>%s</DeviceID>
</Query>
`
// DeviceStatusXML 查询设备详情xml样式
DeviceStatusXML = `<?xml version="1.0" encoding="%s"?>
<Query>
<CmdType>DeviceStatus</CmdType>
<SN>%d</SN>
<DeviceID>%s</DeviceID>
</Query>
`
// DevicePositionXML 订阅设备位置
DevicePositionXML = `<?xml version="1.0"?>
@@ -98,6 +106,11 @@ func BuildDeviceInfoXML(sn int, id string, charset string) []byte {
return toGB2312(fmt.Sprintf(DeviceInfoXML, charset, sn, id))
}
// BuildDeviceStatusXML 获取设备详情指令
func BuildDeviceStatusXML(sn int, id string, charset string) []byte {
return toGB2312(fmt.Sprintf(DeviceStatusXML, charset, sn, id))
}
// BuildCatalogXML 获取NVR下设备列表指令
func BuildCatalogXML(charset string, sn int, id string) []byte {
return toGB2312(fmt.Sprintf(CatalogXML, charset, sn, id))
+152
View File
@@ -289,6 +289,9 @@ func (p *Platform) OnMessage(req *sip.Request, tx sip.ServerTransaction, msg *gb
case "DeviceInfo":
// 处理设备信息请求
return p.handleDeviceInfo(req, tx, msg)
case "DeviceStatus":
// 处理设备信息请求
return p.handleDeviceStatus(req, tx, msg)
case "PresetQuery":
// 处理预置位查询请求
return p.handlePresetQuery(req, tx, msg)
@@ -653,6 +656,155 @@ func (p *Platform) handleDeviceControl(req *sip.Request, tx sip.ServerTransactio
return nil
}
// handleDeviceStatus 处理设备状态查询请求
func (p *Platform) handleDeviceStatus(req *sip.Request, tx sip.ServerTransaction, msg *gb28181.Message) error {
// 先回复200 OK
err := tx.Respond(sip.NewResponseFromRequest(req, http.StatusOK, "OK", nil))
if err != nil {
return fmt.Errorf("respond error: %v", err)
}
// 获取 SN 和 FromTag
sn := strconv.Itoa(msg.SN)
fromTag, _ := req.From().Params.Get("tag")
// 获取请求的设备ID
channelId := msg.DeviceID
// 1. 判断是否是查询平台自身信息
if p.PlatformModel.DeviceGBID == channelId {
// 如果是查询平台信息,直接返回平台状态
return p.sendDeviceStatusResponse(req, nil, sn, fromTag)
}
// 2. 查询通道和设备信息
type Result struct {
DeviceID string `gorm:"column:device_id"`
}
var result Result
if p.plugin.DB != nil {
// 多表联查: channel_gb28181pro -> device_gb28181pro -> devices
if err := p.plugin.DB.Table("channel_gb28181pro c").
Select("d.device_id").
Joins("LEFT JOIN device_gb28181pro d ON c.device_db_id = d.id").
Where("c.device_id = ?", channelId).
First(&result).Error; err != nil {
p.Error("查询通道和设备信息失败", "error", err.Error())
return fmt.Errorf("channel or device not found: %v", err)
}
}
// 3. 从devices集合中获取设备实例
device, ok := p.plugin.devices.Get(result.DeviceID)
if !ok {
p.Error("设备不存在或未注册", "device_id", result.DeviceID)
return fmt.Errorf("device not found or not registered: %v", result.DeviceID)
}
// 4. 发送设备状态响应
return p.sendDeviceStatusResponse(req, device, sn, fromTag)
}
// sendDeviceStatusResponse 发送设备状态响应
func (p *Platform) sendDeviceStatusResponse(req *sip.Request, device *Device, sn string, fromTag string) error {
request := p.CreateRequest("MESSAGE")
// 设置From头部
fromHeader := sip.FromHeader{
Address: sip.Uri{
User: p.PlatformModel.DeviceGBID,
Host: p.PlatformModel.ServerGBDomain,
},
Params: sip.NewParams(),
}
fromHeader.Params.Add("tag", fromTag)
request.AppendHeader(&fromHeader)
// 添加To头部
toHeader := sip.ToHeader{
Address: sip.Uri{
User: p.PlatformModel.ServerGBID,
Host: p.PlatformModel.ServerGBDomain,
},
}
request.AppendHeader(&toHeader)
// 添加Via头部
viaHeader := sip.ViaHeader{
ProtocolName: "SIP",
ProtocolVersion: "2.0",
Transport: p.PlatformModel.Transport,
Host: p.PlatformModel.DeviceIP,
Port: p.PlatformModel.DevicePort,
Params: sip.NewParams(),
}
viaHeader.Params.Add("branch", sip.GenerateBranchN(16)).Add("rport", "")
request.AppendHeader(&viaHeader)
// 设置Content-Type
contentTypeHeader := sip.ContentTypeHeader("Application/MANSCDP+xml")
request.AppendHeader(&contentTypeHeader)
// 获取当前时间,格式化为设备时间
currentTime := time.Now().Format("2006-01-02T15:04:05")
// 根据设备状态构建响应
var deviceID, online, status, encode, record string
if device == nil {
// 平台自身状态
deviceID = p.PlatformModel.DeviceGBID
online = "ONLINE"
status = "OK"
encode = "ON"
record = "OFF"
} else {
// 设备状态
deviceID = device.DeviceID
// 将布尔值转换为对应的状态字符串
if device.Online {
online = "ONLINE"
status = "OK"
encode = "ON" // 在线时默认编码开启
record = "OFF" // 默认不录制
} else {
online = "OFFLINE"
status = "ERROR"
encode = "OFF" // 离线时编码关闭
record = "OFF" // 离线时不录制
}
}
// 构建响应XML
xmlContent := fmt.Sprintf(`<?xml version="1.0" encoding="GB2312" standalone="yes" ?>
<Response>
<CmdType>DeviceStatus</CmdType>
<SN>%s</SN>
<DeviceID>%s</DeviceID>
<Result>OK</Result>
<Online>%s</Online>
<Status>%s</Status>
<DeviceTime>%s</DeviceTime>
<Encode>%s</Encode>
<Record>%s</Record>
<Alarmstatus Num="0"/>
</Response>`, sn, deviceID, online, status, currentTime, encode, record)
request.SetBody([]byte(xmlContent))
// 设置传输协议
request.SetTransport(strings.ToUpper(p.PlatformModel.Transport))
// 发送响应
_, err := p.Client.Do(p.ctx, request)
if err != nil {
p.Error("发送设备状态响应失败", "error", err.Error())
return fmt.Errorf("send device status response failed: %v", err)
}
return nil
}
// handleDeviceInfo 处理设备信息查询请求
func (p *Platform) handleDeviceInfo(req *sip.Request, tx sip.ServerTransaction, msg *gb28181.Message) error {
// 先回复200 OK