diff --git a/plugin/gb28181pro/device.go b/plugin/gb28181pro/device.go
index 0ce358c..ab288c6 100644
--- a/plugin/gb28181pro/device.go
+++ b/plugin/gb28181pro/device.go
@@ -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"))
diff --git a/plugin/gb28181pro/index.go b/plugin/gb28181pro/index.go
index 5ce0a92..37a1e85 100644
--- a/plugin/gb28181pro/index.go
+++ b/plugin/gb28181pro/index.go
@@ -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坐标系
diff --git a/plugin/gb28181pro/pkg/message.go b/plugin/gb28181pro/pkg/message.go
index a7443a6..4d910e1 100644
--- a/plugin/gb28181pro/pkg/message.go
+++ b/plugin/gb28181pro/pkg/message.go
@@ -42,6 +42,14 @@ const (
%d
%s
+`
+ // DeviceStatusXML 查询设备详情xml样式
+ DeviceStatusXML = `
+
+DeviceStatus
+%d
+%s
+
`
// DevicePositionXML 订阅设备位置
DevicePositionXML = `
@@ -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))
diff --git a/plugin/gb28181pro/platform.go b/plugin/gb28181pro/platform.go
index 3ff6dda..d949faa 100644
--- a/plugin/gb28181pro/platform.go
+++ b/plugin/gb28181pro/platform.go
@@ -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(`
+
+DeviceStatus
+%s
+%s
+OK
+%s
+%s
+%s
+%s
+%s
+
+`, 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