mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-10-25 06:30:21 +08:00
867 lines
26 KiB
Go
867 lines
26 KiB
Go
package plugin_gb28181pro
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/emiago/sipgo"
|
|
"github.com/emiago/sipgo/sip"
|
|
"github.com/rs/zerolog"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
"m7s.live/v5/pkg/config"
|
|
"m7s.live/v5/plugin/gb28181pro/pb"
|
|
gb28181 "m7s.live/v5/plugin/gb28181pro/pkg"
|
|
)
|
|
|
|
func (gb *GB28181ProPlugin) List(context.Context, *emptypb.Empty) (ret *pb.ResponseList, err error) {
|
|
ret = &pb.ResponseList{}
|
|
for d := range gb.devices.Range {
|
|
var channels []*pb.Channel
|
|
for c := range d.channels.Range {
|
|
channels = append(channels, &pb.Channel{
|
|
DeviceID: c.DeviceID,
|
|
ParentID: c.ParentID,
|
|
Name: c.Name,
|
|
Manufacturer: c.Manufacturer,
|
|
Model: c.Model,
|
|
Owner: c.Owner,
|
|
CivilCode: c.CivilCode,
|
|
Address: c.Address,
|
|
Port: int32(c.Port),
|
|
Parental: int32(c.Parental),
|
|
SafetyWay: int32(c.SafetyWay),
|
|
RegisterWay: int32(c.RegisterWay),
|
|
Secrecy: int32(c.Secrecy),
|
|
Status: string(c.Status),
|
|
Longitude: c.Longitude,
|
|
Latitude: c.Latitude,
|
|
GpsTime: timestamppb.New(c.GpsTime),
|
|
})
|
|
}
|
|
ret.Data = append(ret.Data, &pb.Device{
|
|
Id: d.DeviceID,
|
|
Name: d.Name,
|
|
Manufacturer: d.Manufacturer,
|
|
Model: d.Model,
|
|
Owner: d.Owner,
|
|
Status: string(d.Status),
|
|
Longitude: d.Longitude,
|
|
Latitude: d.Latitude,
|
|
GpsTime: timestamppb.New(d.GpsTime),
|
|
RegisterTime: timestamppb.New(d.StartTime),
|
|
UpdateTime: timestamppb.New(d.UpdateTime),
|
|
Channels: channels,
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
func (gb *GB28181ProPlugin) api_ps_replay(w http.ResponseWriter, r *http.Request) {
|
|
dump := r.URL.Query().Get("dump")
|
|
streamPath := r.PathValue("streamPath")
|
|
if dump == "" {
|
|
dump = "dump/ps"
|
|
}
|
|
if streamPath == "" {
|
|
if strings.HasPrefix(dump, "/") {
|
|
streamPath = "replay" + dump
|
|
} else {
|
|
streamPath = "replay/" + dump
|
|
}
|
|
}
|
|
var puller gb28181.DumpPuller
|
|
puller.GetPullJob().Init(&puller, &gb.Plugin, streamPath, config.Pull{
|
|
URL: dump,
|
|
}, nil)
|
|
}
|
|
|
|
// GetDevice 实现获取单个设备信息
|
|
func (gb *GB28181ProPlugin) GetDevice(ctx context.Context, req *pb.GetDeviceRequest) (*pb.DeviceResponse, error) {
|
|
resp := &pb.DeviceResponse{}
|
|
|
|
// 先从内存中获取
|
|
d, ok := gb.devices.Get(req.DeviceId)
|
|
if !ok && gb.DB != nil {
|
|
// 如果内存中没有且数据库存在,则从数据库查询
|
|
var device Device
|
|
if err := gb.DB.Where("id = ?", req.DeviceId).First(&device).Error; err == nil {
|
|
d = &device
|
|
}
|
|
}
|
|
|
|
if d != nil {
|
|
var channels []*pb.Channel
|
|
for c := range d.channels.Range {
|
|
channels = append(channels, &pb.Channel{
|
|
DeviceID: c.DeviceID,
|
|
ParentID: c.ParentID,
|
|
Name: c.Name,
|
|
Manufacturer: c.Manufacturer,
|
|
Model: c.Model,
|
|
Owner: c.Owner,
|
|
CivilCode: c.CivilCode,
|
|
Address: c.Address,
|
|
Port: int32(c.Port),
|
|
Parental: int32(c.Parental),
|
|
SafetyWay: int32(c.SafetyWay),
|
|
RegisterWay: int32(c.RegisterWay),
|
|
Secrecy: int32(c.Secrecy),
|
|
Status: string(c.Status),
|
|
Longitude: c.Longitude,
|
|
Latitude: c.Latitude,
|
|
GpsTime: timestamppb.New(c.GpsTime),
|
|
})
|
|
}
|
|
resp.Data = &pb.Device{
|
|
Id: d.DeviceID,
|
|
Name: d.Name,
|
|
Manufacturer: d.Manufacturer,
|
|
Model: d.Model,
|
|
Owner: d.Owner,
|
|
Status: string(d.Status),
|
|
Longitude: d.Longitude,
|
|
Latitude: d.Latitude,
|
|
GpsTime: timestamppb.New(d.GpsTime),
|
|
RegisterTime: timestamppb.New(d.RegisterTime),
|
|
UpdateTime: timestamppb.New(d.UpdateTime),
|
|
Channels: channels,
|
|
}
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
} else {
|
|
resp.Code = 404
|
|
resp.Message = "device not found"
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// GetDevices 实现分页查询设备列表
|
|
func (gb *GB28181ProPlugin) GetDevices(ctx context.Context, req *pb.GetDevicesRequest) (*pb.DevicesPageInfo, error) {
|
|
resp := &pb.DevicesPageInfo{}
|
|
var devices []*pb.Device
|
|
total := 0
|
|
for d := range gb.devices.Range {
|
|
// TODO: 实现查询条件过滤
|
|
if req.Query != "" && !strings.Contains(d.DeviceID, req.Query) && !strings.Contains(d.Name, req.Query) {
|
|
continue
|
|
}
|
|
if req.Status && string(d.Status) != "ON" {
|
|
continue
|
|
}
|
|
total++
|
|
// 分页处理
|
|
if total > int(req.Page*req.Count) {
|
|
continue
|
|
}
|
|
if total <= int((req.Page-1)*req.Count) {
|
|
continue
|
|
}
|
|
var channels []*pb.Channel
|
|
for c := range d.channels.Range {
|
|
channels = append(channels, &pb.Channel{
|
|
DeviceID: c.DeviceID,
|
|
ParentID: c.ParentID,
|
|
Name: c.Name,
|
|
Manufacturer: c.Manufacturer,
|
|
Model: c.Model,
|
|
Owner: c.Owner,
|
|
CivilCode: c.CivilCode,
|
|
Address: c.Address,
|
|
Port: int32(c.Port),
|
|
Parental: int32(c.Parental),
|
|
SafetyWay: int32(c.SafetyWay),
|
|
RegisterWay: int32(c.RegisterWay),
|
|
Secrecy: int32(c.Secrecy),
|
|
Status: string(c.Status),
|
|
Longitude: c.Longitude,
|
|
Latitude: c.Latitude,
|
|
GpsTime: timestamppb.New(c.GpsTime),
|
|
})
|
|
}
|
|
devices = append(devices, &pb.Device{
|
|
Id: d.DeviceID,
|
|
Name: d.Name,
|
|
Manufacturer: d.Manufacturer,
|
|
Model: d.Model,
|
|
Owner: d.Owner,
|
|
Status: string(d.Status),
|
|
Longitude: d.Longitude,
|
|
Latitude: d.Latitude,
|
|
GpsTime: timestamppb.New(d.GpsTime),
|
|
RegisterTime: timestamppb.New(d.StartTime),
|
|
UpdateTime: timestamppb.New(d.UpdateTime),
|
|
Channels: channels,
|
|
})
|
|
}
|
|
resp.Total = int32(total)
|
|
resp.List = devices
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
return resp, nil
|
|
}
|
|
|
|
// GetChannels 实现分页查询通道
|
|
func (gb *GB28181ProPlugin) GetChannels(ctx context.Context, req *pb.GetChannelsRequest) (*pb.ChannelsPageInfo, error) {
|
|
resp := &pb.ChannelsPageInfo{}
|
|
|
|
// 先从内存中获取
|
|
d, ok := gb.devices.Get(req.DeviceId)
|
|
if !ok && gb.DB != nil {
|
|
// 如果内存中没有且数据库存在,则从数据库查询
|
|
var device Device
|
|
if err := gb.DB.Where("id = ?", req.DeviceId).First(&device).Error; err == nil {
|
|
d = &device
|
|
}
|
|
}
|
|
|
|
if d != nil {
|
|
var channels []*pb.Channel
|
|
total := 0
|
|
for c := range d.channels.Range {
|
|
// TODO: 实现查询条件过滤
|
|
if req.Query != "" && !strings.Contains(c.DeviceID, req.Query) && !strings.Contains(c.Name, req.Query) {
|
|
continue
|
|
}
|
|
if req.Online && string(c.Status) != "ON" {
|
|
continue
|
|
}
|
|
if req.ChannelType && c.ParentID == "" {
|
|
continue
|
|
}
|
|
total++
|
|
// 分页处理
|
|
if total > int(req.Page*req.Count) {
|
|
continue
|
|
}
|
|
if total <= int((req.Page-1)*req.Count) {
|
|
continue
|
|
}
|
|
channels = append(channels, &pb.Channel{
|
|
DeviceID: c.DeviceID,
|
|
ParentID: c.ParentID,
|
|
Name: c.Name,
|
|
Manufacturer: c.Manufacturer,
|
|
Model: c.Model,
|
|
Owner: c.Owner,
|
|
CivilCode: c.CivilCode,
|
|
Address: c.Address,
|
|
Port: int32(c.Port),
|
|
Parental: int32(c.Parental),
|
|
SafetyWay: int32(c.SafetyWay),
|
|
RegisterWay: int32(c.RegisterWay),
|
|
Secrecy: int32(c.Secrecy),
|
|
Status: string(c.Status),
|
|
Longitude: c.Longitude,
|
|
Latitude: c.Latitude,
|
|
GpsTime: timestamppb.New(c.GpsTime),
|
|
})
|
|
}
|
|
resp.Total = int32(total)
|
|
resp.List = channels
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
} else {
|
|
resp.Code = 404
|
|
resp.Message = "device not found"
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// SyncDevice 实现同步设备通道信息
|
|
func (gb *GB28181ProPlugin) SyncDevice(ctx context.Context, req *pb.SyncDeviceRequest) (*pb.SyncStatus, error) {
|
|
resp := &pb.SyncStatus{
|
|
Code: 404,
|
|
Message: "device not found",
|
|
}
|
|
|
|
// 先从内存中获取设备
|
|
d, ok := gb.devices.Get(req.DeviceId)
|
|
if !ok && gb.DB != nil {
|
|
// 如果内存中没有且数据库存在,则从数据库查询
|
|
var device Device
|
|
if err := gb.DB.Where("id = ?", req.DeviceId).First(&device).Error; err == nil {
|
|
d = &device
|
|
// 恢复设备的必要字段
|
|
d.Logger = gb.With("id", req.DeviceId)
|
|
d.channels.L = new(sync.RWMutex)
|
|
d.plugin = gb
|
|
|
|
// 初始化 Task
|
|
var hash uint32
|
|
for i := 0; i < len(d.DeviceID); i++ {
|
|
ch := d.DeviceID[i]
|
|
hash = hash*31 + uint32(ch)
|
|
}
|
|
d.Task.ID = hash
|
|
d.Task.Logger = d.Logger
|
|
d.Task.Context, d.Task.CancelCauseFunc = context.WithCancelCause(context.Background())
|
|
|
|
// 初始化 SIP 相关字段
|
|
d.fromHDR = sip.FromHeader{
|
|
Address: sip.Uri{
|
|
User: gb.Serial,
|
|
Host: gb.Realm,
|
|
},
|
|
Params: sip.NewParams(),
|
|
}
|
|
d.fromHDR.Params.Add("tag", sip.GenerateTagN(16))
|
|
|
|
d.contactHDR = sip.ContactHeader{
|
|
Address: sip.Uri{
|
|
User: gb.Serial,
|
|
Host: d.LocalIP,
|
|
Port: d.Port,
|
|
},
|
|
}
|
|
|
|
d.Recipient = sip.Uri{
|
|
Host: d.IP,
|
|
Port: d.Port,
|
|
User: d.DeviceID,
|
|
}
|
|
|
|
// 初始化 SIP 客户端
|
|
d.client, _ = sipgo.NewClient(gb.ua, sipgo.WithClientLogger(zerolog.New(os.Stdout)), sipgo.WithClientHostname(d.LocalIP))
|
|
if d.client != nil {
|
|
d.dialogClient = sipgo.NewDialogClient(d.client, d.contactHDR)
|
|
} else {
|
|
return resp, fmt.Errorf("failed to create sip client")
|
|
}
|
|
|
|
// 将设备添加到内存中
|
|
gb.devices.Add(d)
|
|
}
|
|
}
|
|
|
|
if d != nil {
|
|
// 发送目录查询请求
|
|
_, err := d.catalog()
|
|
if err != nil {
|
|
resp.Code = 500
|
|
resp.Message = "catalog request failed"
|
|
resp.ErrorMsg = err.Error()
|
|
} else {
|
|
resp.Code = 0
|
|
resp.Message = "sync request sent"
|
|
resp.Total = int32(d.ChannelCount)
|
|
resp.Current = 0 // 初始化进度为0
|
|
}
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// StartPlay 处理 StartPlay 请求,用于播放视频流
|
|
func (gb *GB28181ProPlugin) StartPlay(ctx context.Context, req *pb.PlayRequest) (*pb.PlayResponse, error) {
|
|
resp := &pb.PlayResponse{}
|
|
gb.Info("StartPlay request", "deviceId", req.DeviceId, "channelId", req.ChannelId)
|
|
|
|
// 从设备列表中获取设备
|
|
device, ok := gb.devices.Get(req.DeviceId)
|
|
if !ok {
|
|
gb.Error("StartPlay failed", "error", "device not found", "deviceId", req.DeviceId)
|
|
resp.Code = 404
|
|
resp.Message = "device not found"
|
|
return resp, nil
|
|
}
|
|
|
|
// 从设备中获取通道
|
|
channel, ok := device.channels.Get(req.ChannelId)
|
|
if !ok {
|
|
gb.Error("StartPlay failed", "error", "channel not found", "channelId", req.ChannelId)
|
|
resp.Code = 404
|
|
resp.Message = "channel not found"
|
|
return resp, nil
|
|
}
|
|
|
|
// 获取可用的媒体端口
|
|
var mediaPort uint16
|
|
if gb.MediaPort.Valid() {
|
|
select {
|
|
case mediaPort = <-gb.tcpPorts:
|
|
defer func() {
|
|
gb.tcpPorts <- mediaPort
|
|
}()
|
|
default:
|
|
resp.Code = 500
|
|
resp.Message = "no available tcp port"
|
|
return resp, nil
|
|
}
|
|
} else {
|
|
mediaPort = gb.MediaPort[0]
|
|
}
|
|
|
|
// 调用 PlayStreamCmd
|
|
session, err := gb.PlayStreamCmd(device, channel, mediaPort)
|
|
if err != nil {
|
|
gb.Error("StartPlay failed", "error", err, "step", "PlayStreamCmd")
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("play stream failed: %v", err)
|
|
return resp, nil
|
|
}
|
|
|
|
// 等待应答
|
|
err = session.WaitAnswer(gb, sipgo.AnswerOptions{})
|
|
if err != nil {
|
|
gb.Error("StartPlay failed", "error", err, "step", "WaitAnswer")
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("wait answer failed: %v", err)
|
|
return resp, nil
|
|
}
|
|
|
|
// 解析应答中的 SSRC
|
|
inviteResponseBody := string(session.InviteResponse.Body())
|
|
gb.Debug("StartPlay response", "body", inviteResponseBody)
|
|
lines := strings.Split(inviteResponseBody, "\r\n")
|
|
var ssrc string
|
|
for _, line := range lines {
|
|
if parts := strings.Split(line, "="); len(parts) > 1 {
|
|
if parts[0] == "y" && len(parts[1]) > 0 {
|
|
ssrc = parts[1]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// 发送 ACK
|
|
err = session.Ack(gb)
|
|
if err != nil {
|
|
gb.Error("StartPlay failed", "error", err, "step", "Ack")
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("send ack failed: %v", err)
|
|
return resp, nil
|
|
}
|
|
|
|
// 设置响应信息
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
resp.StreamInfo = &pb.StreamInfo{
|
|
Stream: fmt.Sprintf("%s/%s", req.DeviceId, req.ChannelId),
|
|
App: "gb28181",
|
|
Ip: device.IP,
|
|
Port: int32(mediaPort),
|
|
Ssrc: ssrc,
|
|
}
|
|
|
|
gb.Info("StartPlay success", "deviceId", req.DeviceId, "channelId", req.ChannelId, "ssrc", ssrc)
|
|
return resp, nil
|
|
}
|
|
|
|
// AddPlatform 实现添加平台信息
|
|
func (gb *GB28181ProPlugin) AddPlatform(ctx context.Context, req *pb.Platform) (*pb.BaseResponse, error) {
|
|
resp := &pb.BaseResponse{}
|
|
|
|
if gb.DB == nil {
|
|
resp.Code = 500
|
|
resp.Message = "database not initialized"
|
|
return resp, nil
|
|
}
|
|
|
|
// 必填字段校验
|
|
if req.Name == "" {
|
|
resp.Code = 400
|
|
resp.Message = "平台名称不可为空"
|
|
return resp, nil
|
|
}
|
|
if req.ServerGBId == "" {
|
|
resp.Code = 400
|
|
resp.Message = "上级平台国标编号不可为空"
|
|
return resp, nil
|
|
}
|
|
if req.ServerIp == "" {
|
|
resp.Code = 400
|
|
resp.Message = "上级平台IP不可为空"
|
|
return resp, nil
|
|
}
|
|
if req.ServerPort <= 0 || req.ServerPort > 65535 {
|
|
resp.Code = 400
|
|
resp.Message = "上级平台端口异常"
|
|
return resp, nil
|
|
}
|
|
if req.DeviceGBId == "" {
|
|
resp.Code = 400
|
|
resp.Message = "本平台国标编号不可为空"
|
|
return resp, nil
|
|
}
|
|
|
|
// 检查平台是否已存在
|
|
var existingPlatform gb28181.Platform
|
|
if err := gb.DB.Where("server_gb_id = ?", req.ServerGBId).First(&existingPlatform).Error; err == nil {
|
|
resp.Code = 400
|
|
resp.Message = fmt.Sprintf("平台 %s 已存在", req.ServerGBId)
|
|
return resp, nil
|
|
}
|
|
|
|
// 设置默认值
|
|
if req.ServerGBDomain == "" {
|
|
req.ServerGBDomain = req.ServerGBId[:6] // 取前6位作为域
|
|
}
|
|
if req.Expires <= 0 {
|
|
req.Expires = 3600 // 默认3600秒
|
|
}
|
|
if req.KeepTimeout <= 0 {
|
|
req.KeepTimeout = 60 // 默认60秒
|
|
}
|
|
if req.Transport == "" {
|
|
req.Transport = "UDP" // 默认UDP
|
|
}
|
|
if req.CharacterSet == "" {
|
|
req.CharacterSet = "GB2312" // 默认GB2312
|
|
}
|
|
|
|
// 设置创建时间和更新时间
|
|
currentTime := time.Now().Format("2006-01-02 15:04:05")
|
|
req.CreateTime = currentTime
|
|
req.UpdateTime = currentTime
|
|
|
|
// 将proto消息转换为数据库模型
|
|
platform := &gb28181.Platform{
|
|
Enable: req.Enable,
|
|
Name: req.Name,
|
|
ServerGBID: req.ServerGBId,
|
|
ServerGBDomain: req.ServerGBDomain,
|
|
ServerIP: req.ServerIp,
|
|
ServerPort: int(req.ServerPort),
|
|
DeviceGBID: req.DeviceGBId,
|
|
DeviceIP: req.DeviceIp,
|
|
DevicePort: int(req.DevicePort),
|
|
Username: req.Username,
|
|
Password: req.Password,
|
|
Expires: int(req.Expires),
|
|
KeepTimeout: int(req.KeepTimeout),
|
|
Transport: req.Transport,
|
|
CharacterSet: req.CharacterSet,
|
|
PTZ: req.Ptz,
|
|
RTCP: req.Rtcp,
|
|
Status: req.Status,
|
|
ChannelCount: int(req.ChannelCount),
|
|
CatalogSubscribe: req.CatalogSubscribe,
|
|
AlarmSubscribe: req.AlarmSubscribe,
|
|
MobilePositionSubscribe: req.MobilePositionSubscribe,
|
|
CatalogGroup: int(req.CatalogGroup),
|
|
UpdateTime: req.UpdateTime,
|
|
CreateTime: req.CreateTime,
|
|
AsMessageChannel: req.AsMessageChannel,
|
|
SendStreamIP: req.SendStreamIp,
|
|
AutoPushChannel: &req.AutoPushChannel,
|
|
CatalogWithPlatform: int(req.CatalogWithPlatform),
|
|
CatalogWithGroup: int(req.CatalogWithGroup),
|
|
CatalogWithRegion: int(req.CatalogWithRegion),
|
|
CivilCode: req.CivilCode,
|
|
Manufacturer: req.Manufacturer,
|
|
Model: req.Model,
|
|
Address: req.Address,
|
|
RegisterWay: int(req.RegisterWay),
|
|
Secrecy: int(req.Secrecy),
|
|
}
|
|
|
|
// 保存到数据库
|
|
if err := gb.DB.Create(platform).Error; err != nil {
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("failed to create platform: %v", err)
|
|
return resp, nil
|
|
}
|
|
|
|
// 如果平台启用,则启动注册任务
|
|
if platform.Enable {
|
|
// 获取本地SIP端口
|
|
localPort := 5060 // 默认端口
|
|
if len(gb.Sip.ListenAddr) > 0 {
|
|
if port, err := strconv.Atoi(strings.Split(gb.Sip.ListenAddr[0], ":")[1]); err == nil {
|
|
localPort = port
|
|
}
|
|
}
|
|
// 创建平台命令器
|
|
commander := gb28181.NewSIPPlatformCommander(platform, gb.GetPublicIP(platform.ServerIP), localPort)
|
|
if err := commander.InitializeSIPClient(gb.ua); err != nil {
|
|
gb.Error("初始化SIP客户端失败", "error", err)
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("初始化SIP客户端失败: %v", err)
|
|
return resp, nil
|
|
}
|
|
// 启动注册任务
|
|
commander.StartRegisterTask(gb)
|
|
}
|
|
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
return resp, nil
|
|
}
|
|
|
|
// GetPlatform 实现获取平台信息
|
|
func (gb *GB28181ProPlugin) GetPlatform(ctx context.Context, req *pb.GetPlatformRequest) (*pb.PlatformResponse, error) {
|
|
resp := &pb.PlatformResponse{}
|
|
|
|
if gb.DB == nil {
|
|
resp.Code = 500
|
|
resp.Message = "database not initialized"
|
|
return resp, nil
|
|
}
|
|
|
|
var platform gb28181.Platform
|
|
if err := gb.DB.First(&platform, req.Id).Error; err != nil {
|
|
resp.Code = 404
|
|
resp.Message = "platform not found"
|
|
return resp, nil
|
|
}
|
|
|
|
// 将数据库模型转换为proto消息
|
|
resp.Data = &pb.Platform{
|
|
Id: int32(platform.ID),
|
|
Enable: platform.Enable,
|
|
Name: platform.Name,
|
|
ServerGBId: platform.ServerGBID,
|
|
ServerGBDomain: platform.ServerGBDomain,
|
|
ServerIp: platform.ServerIP,
|
|
ServerPort: int32(platform.ServerPort),
|
|
DeviceGBId: platform.DeviceGBID,
|
|
DeviceIp: platform.DeviceIP,
|
|
DevicePort: int32(platform.DevicePort),
|
|
Username: platform.Username,
|
|
Password: platform.Password,
|
|
Expires: int32(platform.Expires),
|
|
KeepTimeout: int32(platform.KeepTimeout),
|
|
Transport: platform.Transport,
|
|
CharacterSet: platform.CharacterSet,
|
|
Ptz: platform.PTZ,
|
|
Rtcp: platform.RTCP,
|
|
Status: platform.Status,
|
|
ChannelCount: int32(platform.ChannelCount),
|
|
CatalogSubscribe: platform.CatalogSubscribe,
|
|
AlarmSubscribe: platform.AlarmSubscribe,
|
|
MobilePositionSubscribe: platform.MobilePositionSubscribe,
|
|
CatalogGroup: int32(platform.CatalogGroup),
|
|
UpdateTime: platform.UpdateTime,
|
|
CreateTime: platform.CreateTime,
|
|
AsMessageChannel: platform.AsMessageChannel,
|
|
SendStreamIp: platform.SendStreamIP,
|
|
AutoPushChannel: platform.AutoPushChannel != nil && *platform.AutoPushChannel,
|
|
CatalogWithPlatform: int32(platform.CatalogWithPlatform),
|
|
CatalogWithGroup: int32(platform.CatalogWithGroup),
|
|
CatalogWithRegion: int32(platform.CatalogWithRegion),
|
|
CivilCode: platform.CivilCode,
|
|
Manufacturer: platform.Manufacturer,
|
|
Model: platform.Model,
|
|
Address: platform.Address,
|
|
RegisterWay: int32(platform.RegisterWay),
|
|
Secrecy: int32(platform.Secrecy),
|
|
}
|
|
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
return resp, nil
|
|
}
|
|
|
|
// UpdatePlatform 实现更新平台信息
|
|
func (gb *GB28181ProPlugin) UpdatePlatform(ctx context.Context, req *pb.Platform) (*pb.BaseResponse, error) {
|
|
resp := &pb.BaseResponse{}
|
|
|
|
if gb.DB == nil {
|
|
resp.Code = 500
|
|
resp.Message = "database not initialized"
|
|
return resp, nil
|
|
}
|
|
|
|
// 检查平台是否存在
|
|
var platform gb28181.Platform
|
|
if err := gb.DB.First(&platform, req.Id).Error; err != nil {
|
|
resp.Code = 404
|
|
resp.Message = "platform not found"
|
|
return resp, nil
|
|
}
|
|
|
|
// 记录之前的启用状态
|
|
wasEnabled := platform.Enable
|
|
|
|
// 更新平台信息
|
|
platform.Enable = req.Enable
|
|
platform.Name = req.Name
|
|
platform.ServerGBID = req.ServerGBId
|
|
platform.ServerGBDomain = req.ServerGBDomain
|
|
platform.ServerIP = req.ServerIp
|
|
platform.ServerPort = int(req.ServerPort)
|
|
platform.DeviceGBID = req.DeviceGBId
|
|
platform.DeviceIP = req.DeviceIp
|
|
platform.DevicePort = int(req.DevicePort)
|
|
platform.Username = req.Username
|
|
platform.Password = req.Password
|
|
platform.Expires = int(req.Expires)
|
|
platform.KeepTimeout = int(req.KeepTimeout)
|
|
platform.Transport = req.Transport
|
|
platform.CharacterSet = req.CharacterSet
|
|
platform.PTZ = req.Ptz
|
|
platform.RTCP = req.Rtcp
|
|
platform.Status = req.Status
|
|
platform.ChannelCount = int(req.ChannelCount)
|
|
platform.CatalogSubscribe = req.CatalogSubscribe
|
|
platform.AlarmSubscribe = req.AlarmSubscribe
|
|
platform.MobilePositionSubscribe = req.MobilePositionSubscribe
|
|
platform.CatalogGroup = int(req.CatalogGroup)
|
|
platform.UpdateTime = req.UpdateTime
|
|
platform.AsMessageChannel = req.AsMessageChannel
|
|
platform.SendStreamIP = req.SendStreamIp
|
|
platform.AutoPushChannel = &req.AutoPushChannel
|
|
platform.CatalogWithPlatform = int(req.CatalogWithPlatform)
|
|
platform.CatalogWithGroup = int(req.CatalogWithGroup)
|
|
platform.CatalogWithRegion = int(req.CatalogWithRegion)
|
|
platform.CivilCode = req.CivilCode
|
|
platform.Manufacturer = req.Manufacturer
|
|
platform.Model = req.Model
|
|
platform.Address = req.Address
|
|
platform.RegisterWay = int(req.RegisterWay)
|
|
platform.Secrecy = int(req.Secrecy)
|
|
|
|
if err := gb.DB.Save(&platform).Error; err != nil {
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("failed to update platform: %v", err)
|
|
return resp, nil
|
|
}
|
|
|
|
// 处理平台启用状态变化
|
|
if !wasEnabled && platform.Enable {
|
|
// 获取本地SIP端口
|
|
localPort := 5060 // 默认端口
|
|
if len(gb.Sip.ListenAddr) > 0 {
|
|
if port, err := strconv.Atoi(strings.Split(gb.Sip.ListenAddr[0], ":")[1]); err == nil {
|
|
localPort = port
|
|
}
|
|
}
|
|
// 平台从禁用变为启用,启动注册任务
|
|
commander := gb28181.NewSIPPlatformCommander(&platform, gb.GetPublicIP(platform.ServerIP), localPort)
|
|
if err := commander.InitializeSIPClient(gb.ua); err != nil {
|
|
gb.Error("初始化SIP客户端失败", "error", err)
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("初始化SIP客户端失败: %v", err)
|
|
return resp, nil
|
|
}
|
|
commander.StartRegisterTask(gb)
|
|
} else if wasEnabled && !platform.Enable {
|
|
// TODO: 平台从启用变为禁用,需要处理注销逻辑
|
|
// 这里可以添加注销相关的代码
|
|
}
|
|
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
return resp, nil
|
|
}
|
|
|
|
// DeletePlatform 实现删除平台信息
|
|
func (gb *GB28181ProPlugin) DeletePlatform(ctx context.Context, req *pb.DeletePlatformRequest) (*pb.BaseResponse, error) {
|
|
resp := &pb.BaseResponse{}
|
|
|
|
if gb.DB == nil {
|
|
resp.Code = 500
|
|
resp.Message = "database not initialized"
|
|
return resp, nil
|
|
}
|
|
|
|
// 删除平台
|
|
if err := gb.DB.Delete(&gb28181.Platform{}, req.Id).Error; err != nil {
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("failed to delete platform: %v", err)
|
|
return resp, nil
|
|
}
|
|
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
return resp, nil
|
|
}
|
|
|
|
// ListPlatforms 实现获取平台列表
|
|
func (gb *GB28181ProPlugin) ListPlatforms(ctx context.Context, req *pb.ListPlatformsRequest) (*pb.PlatformsPageInfo, error) {
|
|
resp := &pb.PlatformsPageInfo{}
|
|
|
|
if gb.DB == nil {
|
|
resp.Code = 500
|
|
resp.Message = "database not initialized"
|
|
return resp, nil
|
|
}
|
|
|
|
var platforms []gb28181.Platform
|
|
var total int64
|
|
|
|
// 构建查询条件
|
|
query := gb.DB.Model(&gb28181.Platform{})
|
|
if req.Query != "" {
|
|
query = query.Where("name LIKE ? OR server_gb_id LIKE ? OR device_gb_id LIKE ?",
|
|
"%"+req.Query+"%", "%"+req.Query+"%", "%"+req.Query+"%")
|
|
}
|
|
if req.Status {
|
|
query = query.Where("status = ?", true)
|
|
}
|
|
|
|
// 获取总数
|
|
if err := query.Count(&total).Error; err != nil {
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("failed to count platforms: %v", err)
|
|
return resp, nil
|
|
}
|
|
|
|
// 分页查询
|
|
if err := query.Offset(int(req.Page-1) * int(req.Count)).
|
|
Limit(int(req.Count)).
|
|
Find(&platforms).Error; err != nil {
|
|
resp.Code = 500
|
|
resp.Message = fmt.Sprintf("failed to list platforms: %v", err)
|
|
return resp, nil
|
|
}
|
|
|
|
// 转换为proto消息
|
|
var pbPlatforms []*pb.Platform
|
|
for _, p := range platforms {
|
|
pbPlatforms = append(pbPlatforms, &pb.Platform{
|
|
Id: int32(p.ID),
|
|
Enable: p.Enable,
|
|
Name: p.Name,
|
|
ServerGBId: p.ServerGBID,
|
|
ServerGBDomain: p.ServerGBDomain,
|
|
ServerIp: p.ServerIP,
|
|
ServerPort: int32(p.ServerPort),
|
|
DeviceGBId: p.DeviceGBID,
|
|
DeviceIp: p.DeviceIP,
|
|
DevicePort: int32(p.DevicePort),
|
|
Username: p.Username,
|
|
Password: p.Password,
|
|
Expires: int32(p.Expires),
|
|
KeepTimeout: int32(p.KeepTimeout),
|
|
Transport: p.Transport,
|
|
CharacterSet: p.CharacterSet,
|
|
Ptz: p.PTZ,
|
|
Rtcp: p.RTCP,
|
|
Status: p.Status,
|
|
ChannelCount: int32(p.ChannelCount),
|
|
CatalogSubscribe: p.CatalogSubscribe,
|
|
AlarmSubscribe: p.AlarmSubscribe,
|
|
MobilePositionSubscribe: p.MobilePositionSubscribe,
|
|
CatalogGroup: int32(p.CatalogGroup),
|
|
UpdateTime: p.UpdateTime,
|
|
CreateTime: p.CreateTime,
|
|
AsMessageChannel: p.AsMessageChannel,
|
|
SendStreamIp: p.SendStreamIP,
|
|
AutoPushChannel: p.AutoPushChannel != nil && *p.AutoPushChannel,
|
|
CatalogWithPlatform: int32(p.CatalogWithPlatform),
|
|
CatalogWithGroup: int32(p.CatalogWithGroup),
|
|
CatalogWithRegion: int32(p.CatalogWithRegion),
|
|
CivilCode: p.CivilCode,
|
|
Manufacturer: p.Manufacturer,
|
|
Model: p.Model,
|
|
Address: p.Address,
|
|
RegisterWay: int32(p.RegisterWay),
|
|
Secrecy: int32(p.Secrecy),
|
|
})
|
|
}
|
|
|
|
resp.Total = int32(total)
|
|
resp.List = pbPlatforms
|
|
resp.Code = 0
|
|
resp.Message = "success"
|
|
return resp, nil
|
|
}
|