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