From 0cf80cedbf92dff2b1769c3e00d0033db2bb71d4 Mon Sep 17 00:00:00 2001 From: pg Date: Mon, 3 Mar 2025 09:23:57 +0800 Subject: [PATCH] fix: catalog get channellist --- plugin/gb28181pro/pkg/commongbchannel.go | 86 +++--- plugin/gb28181pro/pkg/platformchannel.go | 12 +- plugin/gb28181pro/platform.go | 374 +++++++++++++---------- 3 files changed, 261 insertions(+), 211 deletions(-) diff --git a/plugin/gb28181pro/pkg/commongbchannel.go b/plugin/gb28181pro/pkg/commongbchannel.go index 9eeed05..9bd1615 100644 --- a/plugin/gb28181pro/pkg/commongbchannel.go +++ b/plugin/gb28181pro/pkg/commongbchannel.go @@ -7,133 +7,133 @@ import ( // CommonGBChannel 通用国标通道信息 type CommonGBChannel struct { // 数据库自增ID - GbID int `json:"gbId" gorm:"column:gb_id"` + GbID int `json:"gb_id" gorm:"column:gb_id"` // 国标编码 - GbDeviceID string `json:"gbDeviceId" gorm:"column:gb_device_id"` + GbDeviceID string `json:"gb_device_id" gorm:"column:gb_device_id;default:null"` // 国标名称 - GbName string `json:"gbName" gorm:"column:gb_name"` + GbName string `json:"gb_name" gorm:"column:gb_name;default:null"` // 国标设备厂商 - GbManufacturer string `json:"gbManufacturer" gorm:"column:gb_manufacturer"` + GbManufacturer string `json:"gb_manufacturer" gorm:"column:gb_manufacturer;default:null"` // 国标设备型号 - GbModel string `json:"gbModel" gorm:"column:gb_model"` + GbModel string `json:"gb_model" gorm:"column:gb_model;default:null"` // 国标设备归属 - GbOwner string `json:"gbOwner" gorm:"column:gb_owner"` + GbOwner string `json:"gb_owner" gorm:"column:gb_owner;default:null"` // 国标行政区域 - GbCivilCode string `json:"gbCivilCode" gorm:"column:gb_civil_code"` + GbCivilCode string `json:"gb_civil_code" gorm:"column:gb_civil_code"` // 国标警区 - GbBlock string `json:"gbBlock" gorm:"column:gb_block"` + GbBlock string `json:"gb_block" gorm:"column:gb_block"` // 国标安装地址 - GbAddress string `json:"gbAddress" gorm:"column:gb_address"` + GbAddress string `json:"gb_address" gorm:"column:gb_address;default:null"` // 国标是否有子设备 - GbParental int `json:"gbParental" gorm:"column:gb_parental"` + GbParental int `json:"gb_parental" gorm:"column:gb_parental"` // 国标父节点ID - GbParentID string `json:"gbParentId" gorm:"column:gb_parent_id"` + GbParentID string `json:"gb_parent_id" gorm:"column:gb_parent_id"` // 国标信令安全模式 - GbSafetyWay int `json:"gbSafetyWay" gorm:"column:gb_safety_way"` + GbSafetyWay int `json:"gb_safety_way" gorm:"column:gb_safety_way"` // 国标注册方式 - GbRegisterWay int `json:"gbRegisterWay" gorm:"column:gb_register_way"` + GbRegisterWay int `json:"gb_register_way" gorm:"column:gb_register_way"` // 国标证书序列号 - GbCertNum string `json:"gbCertNum" gorm:"column:gb_cert_num"` + GbCertNum string `json:"gb_cert_num" gorm:"column:gb_cert_num"` // 国标证书有效标识 - GbCertifiable int `json:"gbCertifiable" gorm:"column:gb_certifiable"` + GbCertifiable int `json:"gb_certifiable" gorm:"column:gb_certifiable"` // 国标无效原因码 - GbErrCode int `json:"gbErrCode" gorm:"column:gb_err_code"` + GbErrCode int `json:"gb_err_code" gorm:"column:gb_err_code"` // 国标证书终止有效期 - GbEndTime string `json:"gbEndTime" gorm:"column:gb_end_time"` + GbEndTime string `json:"gb_end_time" gorm:"column:gb_end_time"` // 国标保密属性 - GbSecrecy int `json:"gbSecrecy" gorm:"column:gb_secrecy"` + GbSecrecy int `json:"gb_secrecy" gorm:"column:gb_secrecy"` // 国标IP地址 - GbIPAddress string `json:"gbIpAddress" gorm:"column:gb_ip_address"` + GbIPAddress string `json:"gb_ip_address" gorm:"column:gb_ip_address"` // 国标端口 - GbPort int `json:"gbPort" gorm:"column:gb_port"` + GbPort int `json:"gb_port" gorm:"column:gb_port"` // 国标密码 - GbPassword string `json:"gbPassword" gorm:"column:gb_password"` + GbPassword string `json:"gb_password" gorm:"column:gb_password"` // 国标状态 - GbStatus string `json:"gbStatus" gorm:"column:gb_status"` + GbStatus string `json:"gb_status" gorm:"column:gb_status"` // 国标经度 - GbLongitude float64 `json:"gbLongitude" gorm:"column:gb_longitude"` + GbLongitude float64 `json:"gb_longitude" gorm:"column:gb_longitude"` // 国标纬度 - GbLatitude float64 `json:"gbLatitude" gorm:"column:gb_latitude"` + GbLatitude float64 `json:"gb_latitude" gorm:"column:gb_latitude"` // 国标业务分组ID - GbBusinessGroupID string `json:"gbBusinessGroupId" gorm:"column:gb_business_group_id"` + GbBusinessGroupID string `json:"gb_business_group_id" gorm:"column:gb_business_group_id"` // 国标云台类型 - GbPTZType int `json:"gbPtzType" gorm:"column:gb_ptz_type"` + GbPTZType int `json:"gb_ptz_type" gorm:"column:gb_ptz_type"` // 国标位置类型 - GbPositionType int `json:"gbPositionType" gorm:"column:gb_position_type"` + GbPositionType int `json:"gb_position_type" gorm:"column:gb_position_type"` // 国标房间类型 - GbRoomType int `json:"gbRoomType" gorm:"column:gb_room_type"` + GbRoomType int `json:"gb_room_type" gorm:"column:gb_room_type"` // 国标用途类型 - GbUseType int `json:"gbUseType" gorm:"column:gb_use_type"` + GbUseType int `json:"gb_use_type" gorm:"column:gb_use_type"` // 国标补光类型 - GbSupplyLightType int `json:"gbSupplyLightType" gorm:"column:gb_supply_light_type"` + GbSupplyLightType int `json:"gb_supply_light_type" gorm:"column:gb_supply_light_type"` // 国标方向类型 - GbDirectionType int `json:"gbDirectionType" gorm:"column:gb_direction_type"` + GbDirectionType int `json:"gb_direction_type" gorm:"column:gb_direction_type"` // 国标分辨率 - GbResolution string `json:"gbResolution" gorm:"column:gb_resolution"` + GbResolution string `json:"gb_resolution" gorm:"column:gb_resolution"` // 国标下载速度 - GbDownloadSpeed string `json:"gbDownloadSpeed" gorm:"column:gb_download_speed"` + GbDownloadSpeed string `json:"gb_download_speed" gorm:"column:gb_download_speed"` // 国标空域编码能力 - GbSvcSpaceSupportMod int `json:"gbSvcSpaceSupportMod" gorm:"column:gb_svc_space_support_mod"` + GbSvcSpaceSupportMod int `json:"gb_svc_space_support_mod" gorm:"column:gb_svc_space_support_mod"` // 国标时域编码能力 - GbSvcTimeSupportMode int `json:"gbSvcTimeSupportMode" gorm:"column:gb_svc_time_support_mode"` + GbSvcTimeSupportMode int `json:"gb_svc_time_support_mode" gorm:"column:gb_svc_time_support_mode"` // 关联的国标设备数据库ID - GbDeviceDbID int `json:"gbDeviceDbId" gorm:"column:gb_device_db_id"` + GbDeviceDbID int `json:"gb_device_db_id" gorm:"column:gb_device_db_id"` // 二进制保存的录制计划 - RecordPlan int64 `json:"recordPlan" gorm:"column:record_plan"` + RecordPlan int64 `json:"record_plan" gorm:"column:record_plan"` // 关联的推流ID - StreamPushID int `json:"streamPushId" gorm:"column:stream_push_id"` + StreamPushID int `json:"stream_push_id" gorm:"column:stream_push_id"` // 关联的拉流代理ID - StreamProxyID int `json:"streamProxyId" gorm:"column:stream_proxy_id"` + StreamProxyID int `json:"stream_proxy_id" gorm:"column:stream_proxy_id"` // 创建时间 - CreateTime string `json:"createTime" gorm:"column:create_time"` + CreateTime string `json:"create_time" gorm:"column:create_time"` // 更新时间 - UpdateTime string `json:"updateTime" gorm:"column:update_time"` + UpdateTime string `json:"update_time" gorm:"column:update_time"` // 流ID,存在表示正在推流 - StreamID string `json:"streamId" xml:"-"` + StreamID string `json:"stream_id" xml:"-"` // 是否含有音频 - HasAudio bool `json:"hasAudio" xml:"-"` + HasAudio bool `json:"has_audio" xml:"-"` } // Build 构建通道信息 diff --git a/plugin/gb28181pro/pkg/platformchannel.go b/plugin/gb28181pro/pkg/platformchannel.go index 889e6f3..0a64bf6 100644 --- a/plugin/gb28181pro/pkg/platformchannel.go +++ b/plugin/gb28181pro/pkg/platformchannel.go @@ -7,14 +7,14 @@ type PlatformChannel struct { Id int // 数据库自增长ID PlatformId int // 平台ID DeviceChannelId int // 设备通道ID - CustomDeviceId string // 国标-编码 - CustomName string // 国标-名称 - CustomManufacturer string // 国标-设备厂商 - CustomModel string // 国标-设备型号 - CustomOwner string // 国标-设备归属 + CustomDeviceId string `gorm:"default:null"` // 国标-编码 + CustomName string `gorm:"default:null"` // 国标-名称 + CustomManufacturer string `gorm:"default:null"` // 国标-设备厂商 + CustomModel string `gorm:"default:null"` // 国标-设备型号 + CustomOwner string `gorm:"default:null"` // 国标-设备归属 CustomCivilCode string // 国标-行政区域 CustomBlock string // 国标-警区 - CustomAddress string // 国标-安装地址 + CustomAddress string `gorm:"default:null"` // 国标-安装地址 CustomParental int // 国标-是否有子设备 CustomParentId string // 国标-父节点ID CustomSafetyWay int // 国标-信令安全模式 diff --git a/plugin/gb28181pro/platform.go b/plugin/gb28181pro/platform.go index a21686a..5a80ff1 100644 --- a/plugin/gb28181pro/platform.go +++ b/plugin/gb28181pro/platform.go @@ -38,9 +38,11 @@ type Platform struct { eventChan chan any // 插件配置 plugin *GB28181ProPlugin + ctx context.Context } func (p *Platform) init() { + p.ctx = context.Background() client, err := sipgo.NewClient(p.plugin.ua, sipgo.WithClientHostname(p.PlatformModel.DeviceIP), sipgo.WithClientPort(p.PlatformModel.DevicePort)) if err != nil { p.Error("failed to create sip client: %v", err) @@ -212,7 +214,7 @@ func (p *Platform) Unregister(ctx context.Context) (*sipgo.DialogClientSession, req.SetTransport(strings.ToUpper(p.PlatformModel.Transport)) // 发送请求并获取响应 - tx, err := p.Client.TransactionRequest(ctx, req, sipgo.ClientRequestAddVia) + tx, err := p.Client.TransactionRequest(ctx, req) if err != nil { return nil, fmt.Errorf("创建事务失败: %v", err) } @@ -308,6 +310,9 @@ func (p *Platform) handleCatalog(req *sip.Request, tx sip.ServerTransaction, msg fromTag, _ := req.From().Params.Get("tag") p.plugin.Info("catalog", "sn", sn, "fromTag", fromTag) + // 打印平台ID + p.plugin.Info("catalog query platform_id", "platform_id", p.PlatformModel.ID) + // 查询通道列表 var channels []gb28181.CommonGBChannel if p.plugin.DB != nil { @@ -318,40 +323,40 @@ func (p *Platform) handleCatalog(req *sip.Request, tx sip.ServerTransaction, msg c.stream_proxy_id, c.create_time, c.update_time, - COALESCE(pc.custom_device_id, c.gb_device_id, c.device_id) as gb_device_id, - COALESCE(pc.custom_name, c.gb_name, c.name) as gb_name, - COALESCE(pc.custom_manufacturer, c.gb_manufacturer, c.manufacturer) as gb_manufacturer, - COALESCE(pc.custom_model, c.gb_model, c.model) as gb_model, - COALESCE(pc.custom_owner, c.gb_owner, c.owner) as gb_owner, - COALESCE(pc.custom_civil_code, c.gb_civil_code, c.civil_code) as gb_civil_code, - COALESCE(pc.custom_block, c.gb_block, c.block) as gb_block, - COALESCE(pc.custom_address, c.gb_address, c.address) as gb_address, - COALESCE(pc.custom_parental, c.gb_parental, c.parental) as gb_parental, - COALESCE(pc.custom_parent_id, c.gb_parent_id, c.parent_id) as gb_parent_id, - COALESCE(pc.custom_safety_way, c.gb_safety_way, c.safety_way) as gb_safety_way, - COALESCE(pc.custom_register_way, c.gb_register_way, c.register_way) as gb_register_way, - COALESCE(pc.custom_cert_num, c.gb_cert_num, c.cert_num) as gb_cert_num, - COALESCE(pc.custom_certifiable, c.gb_certifiable, c.certifiable) as gb_certifiable, - COALESCE(pc.custom_err_code, c.gb_err_code, c.err_code) as gb_err_code, - COALESCE(pc.custom_end_time, c.gb_end_time, c.end_time) as gb_end_time, - COALESCE(pc.custom_secrecy, c.gb_secrecy, c.secrecy) as gb_secrecy, - COALESCE(pc.custom_ip_address, c.gb_ip_address, c.ip_address) as gb_ip_address, - COALESCE(pc.custom_port, c.gb_port, c.port) as gb_port, - COALESCE(pc.custom_password, c.gb_password, c.password) as gb_password, - COALESCE(pc.custom_status, c.gb_status, c.status) as gb_status, - COALESCE(pc.custom_longitude, c.gb_longitude, c.longitude) as gb_longitude, - COALESCE(pc.custom_latitude, c.gb_latitude, c.latitude) as gb_latitude, - COALESCE(pc.custom_ptz_type, c.gb_ptz_type, c.ptz_type) as gb_ptz_type, - COALESCE(pc.custom_position_type, c.gb_position_type, c.position_type) as gb_position_type, - COALESCE(pc.custom_room_type, c.gb_room_type, c.room_type) as gb_room_type, - COALESCE(pc.custom_use_type, c.gb_use_type, c.use_type) as gb_use_type, - COALESCE(pc.custom_supply_light_type, c.gb_supply_light_type, c.supply_light_type) as gb_supply_light_type, - COALESCE(pc.custom_direction_type, c.gb_direction_type, c.direction_type) as gb_direction_type, - COALESCE(pc.custom_resolution, c.gb_resolution, c.resolution) as gb_resolution, - COALESCE(pc.custom_business_group_id, c.gb_business_group_id, c.business_group_id) as gb_business_group_id, - COALESCE(pc.custom_download_speed, c.gb_download_speed, c.download_speed) as gb_download_speed, - COALESCE(pc.custom_svc_space_support_mod, c.gb_svc_space_support_mod, c.svc_space_support_mod) as gb_svc_space_support_mod, - COALESCE(pc.custom_svc_time_support_mode, c.gb_svc_time_support_mode, c.svc_time_support_mode) as gb_svc_time_support_mode`). + COALESCE(nullif(pc.custom_device_id,''), nullif(c.gb_device_id,''), nullif(c.device_id,'')) as gb_device_id, + COALESCE(nullif(pc.custom_name,''), nullif(c.gb_name,''), nullif(c.name,'')) as gb_name, + COALESCE(nullif(pc.custom_manufacturer,''), nullif(c.gb_manufacturer,''), nullif(c.manufacturer,'')) as gb_manufacturer, + COALESCE(nullif(pc.custom_model,''), nullif(c.gb_model,''), nullif(c.model,'')) as gb_model, + COALESCE(nullif(pc.custom_owner,''), nullif(c.gb_owner,''), nullif(c.owner,'')) as gb_owner, + COALESCE(nullif(pc.custom_civil_code,''), nullif(c.gb_civil_code,''), nullif(c.civil_code,'')) as gb_civil_code, + COALESCE(nullif(pc.custom_block,''), nullif(c.gb_block,''), nullif(c.block,'')) as gb_block, + COALESCE(nullif(pc.custom_address,''), nullif(c.gb_address,''), nullif(c.address,'')) as gb_address, + COALESCE(nullif(pc.custom_parental,''), nullif(c.gb_parental,''), nullif(c.parental,'')) as gb_parental, + COALESCE(nullif(pc.custom_parent_id,''), nullif(c.gb_parent_id,''), nullif(c.parent_id,'')) as gb_parent_id, + COALESCE(nullif(pc.custom_safety_way,''), nullif(c.gb_safety_way,''), nullif(c.safety_way,'')) as gb_safety_way, + COALESCE(nullif(pc.custom_register_way,''), nullif(c.gb_register_way,''), nullif(c.register_way,'')) as gb_register_way, + COALESCE(nullif(pc.custom_cert_num,''), nullif(c.gb_cert_num,''), nullif(c.cert_num,'')) as gb_cert_num, + COALESCE(nullif(pc.custom_certifiable,''), nullif(c.gb_certifiable,''), nullif(c.certifiable,'')) as gb_certifiable, + COALESCE(nullif(pc.custom_err_code,''), nullif(c.gb_err_code,''), nullif(c.err_code,'')) as gb_err_code, + COALESCE(nullif(pc.custom_end_time,''), nullif(c.gb_end_time,''), nullif(c.end_time,'')) as gb_end_time, + COALESCE(nullif(pc.custom_secrecy,''), nullif(c.gb_secrecy,''), nullif(c.secrecy,'')) as gb_secrecy, + COALESCE(nullif(pc.custom_ip_address,''), nullif(c.gb_ip_address,''), nullif(c.ip_address,'')) as gb_ip_address, + COALESCE(nullif(pc.custom_port,''), nullif(c.gb_port,''), nullif(c.port,'')) as gb_port, + COALESCE(nullif(pc.custom_password,''), nullif(c.gb_password,''), nullif(c.password,'')) as gb_password, + COALESCE(nullif(pc.custom_status,''), nullif(c.gb_status,''), nullif(c.status,'')) as gb_status, + COALESCE(nullif(pc.custom_longitude,''), nullif(c.gb_longitude,''), nullif(c.longitude,'')) as gb_longitude, + COALESCE(nullif(pc.custom_latitude,''), nullif(c.gb_latitude,''), nullif(c.latitude,'')) as gb_latitude, + COALESCE(nullif(pc.custom_ptz_type,''), nullif(c.gb_ptz_type,''), nullif(c.ptz_type,'')) as gb_ptz_type, + COALESCE(nullif(pc.custom_position_type,''), nullif(c.gb_position_type,''), nullif(c.position_type,'')) as gb_position_type, + COALESCE(nullif(pc.custom_room_type,''), nullif(c.gb_room_type,''), nullif(c.room_type,'')) as gb_room_type, + COALESCE(nullif(pc.custom_use_type,''), nullif(c.gb_use_type,''), nullif(c.use_type,'')) as gb_use_type, + COALESCE(nullif(pc.custom_supply_light_type,''), nullif(c.gb_supply_light_type,''), nullif(c.supply_light_type,'')) as gb_supply_light_type, + COALESCE(nullif(pc.custom_direction_type,''), nullif(c.gb_direction_type,''), nullif(c.direction_type,'')) as gb_direction_type, + COALESCE(nullif(pc.custom_resolution,''), nullif(c.gb_resolution,''), nullif(c.resolution,'')) as gb_resolution, + COALESCE(nullif(pc.custom_business_group_id,''), nullif(c.gb_business_group_id,''), nullif(c.business_group_id,'')) as gb_business_group_id, + COALESCE(nullif(pc.custom_download_speed,''), nullif(c.gb_download_speed,''), nullif(c.download_speed,'')) as gb_download_speed, + COALESCE(nullif(pc.custom_svc_space_support_mod,''), nullif(c.gb_svc_space_support_mod,''), nullif(c.svc_space_support_mod,'')) as gb_svc_space_support_mod, + COALESCE(nullif(pc.custom_svc_time_support_mode,''), nullif(c.gb_svc_time_support_mode,''), nullif(c.svc_time_support_mode,'')) as gb_svc_time_support_mode`). Joins("left join platform_channel_gb28181pro pc on c.id = pc.device_channel_id"). Where("pc.platform_id = ?", p.PlatformModel.ID). Find(&channels).Error; err != nil { @@ -359,13 +364,9 @@ func (p *Platform) handleCatalog(req *sip.Request, tx sip.ServerTransaction, msg } } - // 发送目录响应 - if len(channels) > 0 { - p.Info("get channels success", channels) - return p.sendCatalogResponse(req, sn, fromTag, channels) - } else { - return p.sendEmptyCatalogResponse(req, sn, fromTag) - } + // 发送目录响应,无论是否有通道 + p.plugin.Info("get channels success", channels) + return p.sendCatalogResponse(req, sn, fromTag, channels) } // CreateRequest 创建 SIP 请求 @@ -377,76 +378,185 @@ func (p *Platform) CreateRequest(method string) *sip.Request { // sendCatalogResponse 发送目录响应 func (p *Platform) sendCatalogResponse(req *sip.Request, sn string, fromTag string, channels []gb28181.CommonGBChannel) error { - request := p.CreateRequest("MESSAGE") + // 如果没有通道,发送一个空的目录列表 + if len(channels) == 0 { + 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, - }, - } - req.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", "") - req.AppendHeader(&viaHeader) + // 设置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) - //request.SetSource(req.Source()) - //request.SetDestination(req.Destination()) - request.SetTransport(req.Transport()) - contentTypeHeader := sip.ContentTypeHeader("Application/MANSCDP+xml") - request.AppendHeader(&contentTypeHeader) - request.SetBody([]byte(fmt.Sprintf(` + // 添加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) + + request.SetTransport(req.Transport()) + contentTypeHeader := sip.ContentTypeHeader("Application/MANSCDP+xml") + request.AppendHeader(&contentTypeHeader) + + // 空目录列表XML + xmlContent := fmt.Sprintf(` + +Catalog +%s +%s +0 + + +`, sn, p.PlatformModel.DeviceGBID) + request.SetBody([]byte(xmlContent)) + _, err := p.Client.Do(p.ctx, request) + if err != nil { + p.plugin.Error("p.Client.Do", err) + } + return err + } + + // 有通道时,为每个通道单独发送一个XML + for i, channel := range channels { + 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) + + request.SetTransport(req.Transport()) + contentTypeHeader := sip.ContentTypeHeader("Application/MANSCDP+xml") + request.AppendHeader(&contentTypeHeader) + + // 为单个通道创建XML + channelXML := p.buildChannelItem(channel) + xmlContent := fmt.Sprintf(` Catalog %s %s %d - + %s -`, sn, p.PlatformModel.DeviceGBID, len(channels), len(channels), p.buildChannelList(channels)))) - _, err := p.Client.Do(p, request) - return err +`, sn, p.PlatformModel.DeviceGBID, len(channels), channelXML) + + request.SetBody([]byte(xmlContent)) + _, err := p.Client.Do(p.ctx, request) + if err != nil { + p.Error("send catalog response", "error", err.Error(), "channel_index", i) + return err + } + + // 添加短暂延迟以防止发送过快 + time.Sleep(time.Millisecond * 50) + } + + return nil } -// sendEmptyCatalogResponse 发送空目录响应 -func (p *Platform) sendEmptyCatalogResponse(req *sip.Request, sn string, fromTag string) error { - request := p.CreateRequest("MESSAGE") - request.From().Params.Add("tag", fromTag) - request.To().Params.Add("tag", fromTag) - request.SetSource(req.Source()) - request.SetDestination(req.Destination()) - request.SetTransport(req.Transport()) - contentTypeHeader := sip.ContentTypeHeader("Application/MANSCDP+xml") - request.AppendHeader(&contentTypeHeader) - request.SetBody([]byte(fmt.Sprintf(` - -Catalog -%s - - -`, sn))) - _, err := p.Client.Do(p, request) - return err +// buildChannelItem 构建单个通道的XML项 +func (p *Platform) buildChannelItem(channel gb28181.CommonGBChannel) string { + // 确保字符串字段不为空 + deviceID := channel.GbDeviceID + if deviceID == "" { + deviceID = "unknown_device" // 如果没有设备ID,使用默认值 + } + name := channel.GbName + if name == "" { + name = "未命名设备" + } + manufacturer := channel.GbManufacturer + if manufacturer == "" { + manufacturer = "未知厂商" + } + model := channel.GbModel + if model == "" { + model = "未知型号" + } + owner := channel.GbOwner + if owner == "" { + owner = "未知所有者" + } + address := channel.GbAddress + if address == "" { + address = "未知地址" + } + parentID := channel.GbParentID + if parentID == "" { + parentID = p.PlatformModel.DeviceGBID // 使用平台ID作为父ID + } + + return fmt.Sprintf(` +%s +%s +%s +%s +%s +
%s
+%d +%d +%s +%d +%d +ON + + +
`, deviceID, name, manufacturer, model, + owner, address, + channel.GbRegisterWay, // 直接使用整数值 + channel.GbSecrecy, // 直接使用整数值 + parentID, + channel.GbParental, // 直接使用整数值 + channel.GbSafetyWay) // 直接使用整数值 } // handleDeviceControl 处理设备控制请求 @@ -592,66 +702,6 @@ func (p *Platform) handleMobilePosition(req *sip.Request, tx sip.ServerTransacti return tx.Respond(response) } -func (p *Platform) buildChannelList(channels []gb28181.CommonGBChannel) string { - var content string - for _, channel := range channels { - // 确保字符串字段不为空 - deviceID := channel.GbDeviceID - if deviceID == "" { - deviceID = "unknown_device" // 如果没有设备ID,使用默认值 - } - name := channel.GbName - if name == "" { - name = "未命名设备" - } - manufacturer := channel.GbManufacturer - if manufacturer == "" { - manufacturer = "未知厂商" - } - model := channel.GbModel - if model == "" { - model = "未知型号" - } - owner := channel.GbOwner - if owner == "" { - owner = "未知所有者" - } - address := channel.GbAddress - if address == "" { - address = "未知地址" - } - parentID := channel.GbParentID - if parentID == "" { - parentID = p.PlatformModel.DeviceGBID // 使用平台ID作为父ID - } - - content += fmt.Sprintf(` -%s -%s -%s -%s -%s -
%s
-%d -%d -%s -%d -%d -ON - - -
-`, deviceID, name, manufacturer, model, - owner, address, - channel.GbRegisterWay, // 直接使用整数值 - channel.GbSecrecy, // 直接使用整数值 - parentID, - channel.GbParental, // 直接使用整数值 - channel.GbSafetyWay) // 直接使用整数值 - } - return content -} - // GetKey 返回平台的唯一标识符 func (p *Platform) GetKey() uint32 { return p.PlatformModel.ID @@ -720,7 +770,7 @@ func (p *Platform) DoRegister(ctx context.Context) error { // 设置传输协议 req.SetTransport(strings.ToUpper(p.PlatformModel.Transport)) - tx, err := p.Client.TransactionRequest(ctx, req, sipgo.ClientRequestAddVia) + tx, err := p.Client.TransactionRequest(ctx, req) if err != nil { p.Error("register", "error", err.Error()) return fmt.Errorf("创建事务失败: %v", err)