Files
monibuca/plugin/gb28181pro/api.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
}