package service
|
|
import (
|
"bytes"
|
"encoding/json"
|
"errors"
|
"fmt"
|
"strconv"
|
"strings"
|
"time"
|
"vamicro/camera-common/models"
|
"vamicro/config"
|
"vamicro/extend/util"
|
gbModels "vamicro/gb28181-service/models"
|
|
"basic.com/gb28181api.git"
|
"basic.com/pubsub/protomsg.git"
|
"basic.com/valib/bhomeclient.git"
|
"basic.com/valib/bhomedbapi.git"
|
"basic.com/valib/logger.git"
|
"github.com/jinzhu/gorm"
|
uuid "github.com/satori/go.uuid"
|
)
|
|
type Gb28181Service struct {
|
bk bhomeclient.Broker
|
}
|
|
func NewGb28181Service(broker bhomeclient.Broker) *Gb28181Service {
|
return &Gb28181Service{
|
bk: broker,
|
}
|
}
|
|
func (s Gb28181Service) Edit(gbConfig gbModels.Gb28181Config) bool {
|
timeUnix := time.Now().Unix()
|
fmtTimeStr := time.Unix(timeUnix, 0).Format("2006-01-02 15:04:05")
|
gbConfig.UpdateTime = fmtTimeStr
|
var gbConf gbModels.Gb28181Config
|
rows, _ := gbConf.Select()
|
if rows > 0 { //已存在,编辑
|
gbConfig.Id = gbConf.Id
|
gbConfig.ServerPort = 8060
|
data := util.Struct2Map(gbConfig)
|
if err := models.GetDB().Table(gbConf.TableName()).Updates(data).Error; err != nil {
|
return false
|
}
|
} else { //新增
|
gbConfig.Id = uuid.NewV4().String()
|
gbConfig.ServerPort = 8060
|
if err := models.GetDB().Table(gbConf.TableName()).Create(&gbConfig).Error; err != nil {
|
return false
|
}
|
}
|
|
setGbIp := getGbIp()
|
if setGbIp != "" {
|
//接入国标的平台树
|
var gbApi gb28181api.Gb28181Api
|
gb28181api.Init(setGbIp, gbConfig.ServerPort)
|
//设置国标平台基础配置
|
gbApi.SetPlatformServerInfo(gb28181api.GbServerInfo{
|
Name: "贝思科国标服务",
|
PublicID: gbConfig.PublicId,
|
GbServerPort: gbConfig.GbServerPort,
|
RtspServrPort: 7554,
|
HTTPClientIP: "",
|
HTTPClientPort: gbConfig.ServerPort,
|
RegisterAuth: gbConfig.IsAuth,
|
GbPasswd: gbConfig.Password,
|
})
|
//先更新资源
|
gbApi.UpdateAllResource()
|
} else {
|
logger.Error("刷新国标服务时,setGbIp不能为空!!!!")
|
return false
|
}
|
|
return true
|
}
|
|
type gbDevAndArea struct {
|
PublicId string
|
ParentId string
|
}
|
|
type VssTable struct {
|
Vd []gbModels.VssDev `json:"vd"`
|
Du map[string]gbModels.VssDomainUnit `json:"du"`
|
Vc map[string]gbModels.VssChannel `json:"vc"`
|
GbCam []models.Camera `json:"gbCam"`
|
}
|
|
//获取国标服务使用的ip(可能是漂移ip)
|
func getGbIp() string {
|
setGbIp := ""
|
localIp, _, _ := util.GetLocalIP(config.Server.NetworkAdapter) //没有配置,则取本机ip
|
var clusterApi bhomedbapi.ClusterApi
|
b, c := clusterApi.FindCluster()
|
logger.Debug("FindCluster b:", b, " c:", c)
|
if b && c.Nodes != nil && len(c.Nodes) > 0 {
|
//在集群内,则非漂移master节点,使用漂移ip,否则使用本机ip。此处漂移master节点,使用漂移ip访问不到本机,这个原因是iptables转换策略改的是本机入的,漂移ip会给到本机的8060,
|
//但是本机出的不走这个策略,所以本机使用漂移ip访问不到本机的8060
|
driftMasterId := ""
|
for _, n := range c.Nodes {
|
if n.DriftState == "master" { //查看当前漂移master节点是否是本机
|
driftMasterId = n.NodeId
|
break
|
}
|
}
|
if driftMasterId != "" {
|
if driftMasterId == config.Server.AnalyServerId { //当前节点是漂移master
|
setGbIp = localIp
|
} else {
|
setGbIp = c.VirtualIp //取漂移ip
|
}
|
}
|
} else {
|
setGbIp = localIp
|
}
|
return setGbIp
|
}
|
|
func (s Gb28181Service) GetAllSubServer() (bool, interface{}) {
|
setGbIp := getGbIp()
|
if setGbIp == "" {
|
return false, errors.New("获取国标ip失败")
|
}
|
var gbConfig gbModels.Gb28181Config
|
rows, _ := gbConfig.Select()
|
if rows == 0 { //已存在,编辑
|
return false, errors.New("请先设置国标配置")
|
}
|
gbConfig.ServerPort = 8060
|
var gbApi gb28181api.Gb28181Api
|
gb28181api.Init(setGbIp, gbConfig.ServerPort)
|
devices, b := gbApi.GetAllDevices()
|
return b, devices
|
}
|
|
// UpdateGb28181Data 从videosvr的sqlite3.db中获取数据, 写入到当前数据库的同步表
|
func (s Gb28181Service) UpdateGb28181Data() (bool, error) {
|
setGbIp := getGbIp()
|
if setGbIp == "" {
|
return false, errors.New("获取国标ip失败")
|
}
|
|
var gbConfig gbModels.Gb28181Config
|
rows, _ := gbConfig.Select()
|
if rows == 0 { //已存在,编辑
|
return false, errors.New("请先设置国标配置")
|
}
|
|
gbConfig.ServerPort = 8060
|
var gbApi gb28181api.Gb28181Api
|
gb28181api.Init(setGbIp, gbConfig.ServerPort)
|
|
var err error
|
tx := models.GetDB().Begin()
|
defer func() {
|
if err != nil && tx != nil {
|
tx.Rollback()
|
logger.Debug("updateGb28181Data err:", err)
|
}
|
}()
|
|
// 清除旧数据,以videosvr数据为准
|
err = tx.Exec("delete from " + gbModels.VssDev{}.TableName() + "").Error
|
if err != nil {
|
return false, err
|
}
|
|
err = tx.Exec("delete from " + gbModels.VssDomainUnit{}.TableName() + "").Error
|
if err != nil {
|
return false, err
|
}
|
|
err = tx.Exec("delete from " + gbModels.VssChannel{}.TableName() + "").Error
|
if err != nil {
|
return false, err
|
}
|
|
// 获取所有下级
|
devices, flag := gbApi.GetAllDevices()
|
logger.Debug("gb28181 group devices:", devices)
|
|
if flag && devices != nil && len(devices) > 0 {
|
if err = batchSaveGbDev(tx, devices); err != nil {
|
return false, err
|
}
|
|
for _, d := range devices {
|
|
// 获取下级的所有通道个目录. 数据来源于vsschannel表, resType = 1 是通道, resType = 2 表示目录
|
allCams, allGroups, b := gbApi.GetAllCamerasAndGroupsByDevID(d.PublicID)
|
logger.Debug("publicId:", d.PublicID, " b:", b, " len(allCams):", len(allCams), " len(allGroups)", len(allGroups))
|
|
if b && len(allCams) > 0 {
|
//t1 := time.Now()
|
if err = batchSaveGbChannel(tx, allCams); err != nil {
|
logger.Debug("batchSaveGbChannel err:", err)
|
return false, err
|
}
|
//logger.Debug("batchSaveGbChannel耗时:", time.Since(t1))
|
}
|
|
// 将resType = 2 的区域信息, 合并到domainUnit表.
|
// 该功能后来被屏蔽过, 应该是和domainUnit的数据有冲突. 一个通道出现了两个组织. 所以干脆以domainUnit为准, 忽略了这里的组织
|
// 但是也有可能导致结构不全, 比如普洱国标整合了这些区域信息后就是正确的.
|
// 可根据不同的结构可以进行调整. 暂时先放开, 保证有尽可能多的的组织结构
|
if len(allGroups) > 0 {
|
//t1 := time.Now()
|
if err = batchSaveGbDomainUnit(tx, allGroups); err != nil {
|
return false, err
|
}
|
//logger.Debug("batchSaveGbDomainUnit耗时:", time.Since(t1))
|
}
|
}
|
}
|
|
// 获取所有国标区域
|
domainUnits, b := gbApi.GetAllDomainUnit()
|
logger.Debug("gb28181 gb units:", len(domainUnits))
|
if b && domainUnits != nil && len(domainUnits) > 0 {
|
if err = batchSaveGbDomainUnit(tx, domainUnits); err != nil {
|
return false, err
|
}
|
}
|
|
tx.Commit()
|
return true, nil
|
}
|
|
func batchSaveGbDev(tx *gorm.DB, dArr []gb28181api.DeviceInfo) error {
|
var buffer bytes.Buffer
|
sql := "insert into `" + gbModels.VssDev{}.TableName() + "` (`publicId`,`name`,`ip`,`port`,`corp`,`parentId`,`username`,`passwd`,`devMode`,`alive`) values "
|
if _, err := buffer.WriteString(sql); err != nil {
|
return err
|
}
|
for i, d := range dArr {
|
if i == len(dArr)-1 {
|
buffer.WriteString(fmt.Sprintf("('%s','%s','%s',%d,'%s','%s','%s','%s','%s',%d);", d.PublicID, d.Name, d.IP, d.Port, d.Corp, d.Parentid, d.Username, d.Passwd, d.Devmode, d.Alive))
|
} else {
|
buffer.WriteString(fmt.Sprintf("('%s','%s','%s',%d,'%s','%s','%s','%s','%s',%d),", d.PublicID, d.Name, d.IP, d.Port, d.Corp, d.Parentid, d.Username, d.Passwd, d.Devmode, d.Alive))
|
}
|
}
|
return tx.Exec(buffer.String()).Error
|
}
|
|
func batchSaveGbChannel(tx *gorm.DB, arr []gb28181api.CameraInfo) error {
|
var buffer bytes.Buffer
|
sql := "insert into `" + gbModels.VssChannel{}.TableName() + "` (`publicId`,`devPubId`,`resType`,`name`,`alive`,`corp`,`model`,`owner`,`civilCode`,`address`,`parentId`,`ip`,`port`,`longitude`,`latitude`,`ptzType`,`streamType`,`realRtspUrl`) values "
|
if _, err := buffer.WriteString(sql); err != nil {
|
return err
|
}
|
for i, c := range arr {
|
if i == len(arr)-1 {
|
buffer.WriteString(fmt.Sprintf("('%s','%s',%d,'%s',%d,'%s','%s','%s','%s','%s','%s','%s',%d,%f,%f,%d,%d,'%s');", c.PublicID, c.DevPubID, c.ResType, c.Name, c.Alive, c.Corp, c.Model, c.Owner, c.CivilCode, c.Address, c.ParentID, c.IP, c.Port, c.Latitude, c.Latitude, c.PtzType, c.StreamType, c.RealRtspURL))
|
} else {
|
buffer.WriteString(fmt.Sprintf("('%s','%s',%d,'%s',%d,'%s','%s','%s','%s','%s','%s','%s',%d,%f,%f,%d,%d,'%s'),", c.PublicID, c.DevPubID, c.ResType, c.Name, c.Alive, c.Corp, c.Model, c.Owner, c.CivilCode, c.Address, c.ParentID, c.IP, c.Port, c.Latitude, c.Latitude, c.PtzType, c.StreamType, c.RealRtspURL))
|
}
|
}
|
return tx.Exec(buffer.String()).Error
|
}
|
|
func batchSaveGbDomainUnit(tx *gorm.DB, uArr []gb28181api.DomainUnit) error {
|
var buffer bytes.Buffer
|
sql := "insert into `" + gbModels.VssDomainUnit{}.TableName() + "` (`publicId`,`devPubId`,`resType`,`name`,`parentId`,`totalNum`,`onlineNum`) values "
|
if _, err := buffer.WriteString(sql); err != nil {
|
return err
|
}
|
for i, u := range uArr {
|
if i == len(uArr)-1 {
|
buffer.WriteString(fmt.Sprintf("('%s','%s',%d,'%s','%s',%d,%d);", u.PublicID, u.DevPubID, u.ResType, u.Name, u.ParentID, u.TotalNum, u.OnlineNum))
|
} else {
|
buffer.WriteString(fmt.Sprintf("('%s','%s',%d,'%s','%s',%d,%d),", u.PublicID, u.DevPubID, u.ResType, u.Name, u.ParentID, u.TotalNum, u.OnlineNum))
|
}
|
}
|
|
return tx.Exec(buffer.String()).Error
|
}
|
|
func GetPidsByRecursion(uIdMap *map[string]gbDevAndArea, parentId string, pIds *string) {
|
if unit, ok := (*uIdMap)[parentId]; ok {
|
*pIds = "," + parentId + "," + *pIds
|
GetPidsByRecursion(uIdMap, unit.ParentId, pIds)
|
} else {
|
*pIds = ",0," + *pIds
|
*pIds = strings.Replace(*pIds, ",,", ",", -1)
|
return
|
}
|
}
|
|
type GbTreeMenu struct {
|
Id string `json:"id"`
|
Name string `json:"name"`
|
Type string `json:"type"`
|
Children []GbTreeMenu `json:"children"`
|
Checked bool `json:"checked"` //是否选中
|
Alive int `json:"alive"` //是否存活,取决于国标库是否动态更新
|
civilCode string
|
parentId string
|
}
|
|
//获取当前国标的目录树结构
|
func (s Gb28181Service) GetGbCurrentTree() []GbTreeMenu {
|
menus := make([]GbTreeMenu, 0)
|
var gbConfig gbModels.Gb28181Config
|
if rows, _ := gbConfig.Select(); rows == 0 {
|
return menus
|
}
|
|
var vssDevices gbModels.VssDev
|
var vssDomainUnit gbModels.VssDomainUnit
|
var vssChannel gbModels.VssChannel
|
devices, _ := vssDevices.FindAll()
|
domainUnits, _ := vssDomainUnit.FindAll()
|
gbAllCamAndGroups, _ := vssChannel.FindAll()
|
|
channelsArry := make([]gbModels.VssChannel, 0)
|
groupArr := make([]gbModels.VssChannel, 0)
|
civilM := make(map[string]gbModels.VssChannel, 0)
|
for _, gcg := range gbAllCamAndGroups {
|
if gcg.ResType == 1 { //摄像机资源
|
channelsArry = append(channelsArry, gcg)
|
} else if gcg.ResType == 2 { //group资源
|
civilM[gcg.CivilCode] = gcg
|
civilM[gcg.PublicID] = gcg
|
groupArr = append(groupArr, gcg)
|
}
|
}
|
|
// 查询当前保存的摄像机, 添加已选择的标志
|
var camCursor models.Camera
|
selectedCams, _ := camCursor.FindCamerasByType(models.TYPE_GB28181_CAMERA, "")
|
logger.Debug("GetGbCurrentTree len(selectedCams):", len(selectedCams))
|
|
ncM := make(map[string]string, 0)
|
selectedCamsMap := make(map[string]models.Camera)
|
for _, gc := range selectedCams {
|
selectedCamsMap[gc.Id] = gc
|
ncM[gc.Id] = gc.Id
|
}
|
|
unitMap := make(map[string]gbModels.VssDomainUnit, 0)
|
for _, u := range domainUnits {
|
unitMap[u.PublicID] = u
|
}
|
|
movedM := make(map[string]string, 0)
|
for _, d := range devices {
|
mu := GbTreeMenu{
|
Id: d.PublicID,
|
Name: d.Name,
|
}
|
if mu.Name == "" {
|
mu.Name = mu.Id
|
}
|
if len(d.PublicID) > 13 && d.PublicID[10:13] == "132" { //表示这个设备是摄像机,并且不在下级平台下
|
mu.Type = "camera"
|
mu.Alive = d.Alive
|
if _, ok := selectedCamsMap[mu.Id]; ok {
|
mu.Checked = true
|
ncM[mu.Id] = mu.Id
|
}
|
movedM[mu.Id] = mu.Id
|
} else {
|
mu.Type = "menu"
|
|
logger.Debug("len(groupArr): ", len(groupArr))
|
rootChk := false
|
for _, tmpChan := range groupArr {
|
if tmpChan.ParentId == d.PublicID {
|
child := GbTreeMenu{
|
Id: tmpChan.PublicID,
|
Name: tmpChan.Name,
|
Type: "menu",
|
parentId: d.PublicID,
|
civilCode: tmpChan.CivilCode,
|
}
|
|
recurseGbMenu(d.PublicID, &child, &domainUnits, unitMap, civilM, &groupArr, &channelsArry, selectedCamsMap, movedM, ncM)
|
if _, chk := ncM[child.Id]; chk {
|
child.Checked = true
|
rootChk = true
|
}
|
mu.Children = append(mu.Children, child)
|
}
|
}
|
|
for _, tmpUnit := range domainUnits {
|
if tmpUnit.ParentID == d.PublicID || tmpUnit.ParentID == "" {
|
child := GbTreeMenu{
|
Id: tmpUnit.PublicID,
|
Name: tmpUnit.Name,
|
Type: "menu",
|
parentId: d.PublicID,
|
civilCode: tmpUnit.PublicID,
|
}
|
|
recurseGbMenu(d.PublicID, &child, &domainUnits, unitMap, civilM, &groupArr, &channelsArry, selectedCamsMap, movedM, ncM)
|
if _, chk := ncM[child.Id]; chk {
|
child.Checked = true
|
rootChk = true
|
}
|
mu.Children = append(mu.Children, child)
|
}
|
}
|
mu.Checked = rootChk
|
}
|
|
menus = append(menus, mu)
|
}
|
|
logger.Debug("len(movedM):", len(movedM), " len(channelsArry):", len(channelsArry))
|
invalidMenu := GbTreeMenu{
|
Id: "invalid",
|
Name: "无上级分组!!!",
|
Type: "menu",
|
parentId: "",
|
}
|
var ignoredPIds []string
|
invalidMenuMap := make(map[string][]GbTreeMenu, 0)
|
for _, c := range channelsArry {
|
if _, ok := movedM[c.PublicID]; !ok {
|
ignoredPIds = append(ignoredPIds, c.PublicID)
|
if cvArr, cvO := invalidMenuMap[c.CivilCode]; !cvO {
|
chk := false
|
if _, chk = ncM[c.PublicID]; chk {
|
chk = true
|
ncM[c.CivilCode] = c.CivilCode
|
}
|
invalidMenuMap[c.CivilCode] = append([]GbTreeMenu{}, GbTreeMenu{
|
Id: c.PublicID,
|
Name: c.Name,
|
Type: "camera",
|
parentId: invalidMenu.Id,
|
Alive: c.Alive,
|
Checked: chk,
|
})
|
} else {
|
chk := false
|
if _, chk = ncM[c.PublicID]; chk {
|
chk = true
|
ncM[c.CivilCode] = c.CivilCode
|
}
|
cvArr = append(cvArr, GbTreeMenu{
|
Id: c.PublicID,
|
Name: c.Name,
|
Type: "camera",
|
parentId: invalidMenu.Id,
|
Alive: c.Alive,
|
Checked: chk,
|
})
|
invalidMenuMap[c.CivilCode] = cvArr
|
}
|
|
movedM[c.PublicID] = c.PublicID
|
}
|
}
|
for k, v := range invalidMenuMap {
|
icName := k
|
if chd, cvIn := civilM[k]; cvIn {
|
icName = chd.Name
|
}
|
|
invalidChild := GbTreeMenu{
|
Id: k,
|
Name: icName,
|
Type: "menu",
|
Children: v,
|
parentId: "invalid",
|
}
|
if _, chk := ncM[invalidChild.Id]; chk {
|
invalidChild.Checked = true
|
invalidMenu.Checked = true
|
}
|
invalidMenu.Children = append(invalidMenu.Children, invalidChild)
|
}
|
|
if len(invalidMenu.Children) > 0 {
|
menus = append(menus, invalidMenu)
|
}
|
|
logger.Debug("len(ignoredIds):", len(ignoredPIds))
|
|
//logger.Debug("menus", menus)
|
|
return menus
|
}
|
|
//获取当前国标的所有摄像机资源信息, 平铺树
|
func (s Gb28181Service) GetGbCameras() []GbTreeMenu {
|
var vssChannel gbModels.VssChannel
|
gbAllCamAndGroups, _ := vssChannel.FindAll()
|
|
channelsArry := make([]GbTreeMenu, 0)
|
for _, gcg := range gbAllCamAndGroups {
|
if gcg.ResType == 1 { //摄像机资源
|
channelsArry = append(channelsArry, GbTreeMenu{
|
Id: gcg.PublicID,
|
Name: gcg.Name,
|
Type: "camera",
|
Children: nil,
|
Checked: false,
|
Alive: gcg.Alive,
|
civilCode: gcg.CivilCode,
|
parentId: gcg.ParentId,
|
})
|
}
|
}
|
|
return channelsArry
|
}
|
|
func recurseGbMenu(devPubId string, mu *GbTreeMenu, uArr *[]gbModels.VssDomainUnit, unitM map[string]gbModels.VssDomainUnit, civilM map[string]gbModels.VssChannel, groupArr *[]gbModels.VssChannel, cArr *[]gbModels.VssChannel, gbcM map[string]models.Camera, movedM map[string]string, ncm map[string]string) {
|
//递归处理所有下级资源组,以及资源组下的摄像机
|
for _, cGroup := range *groupArr {
|
if cGroup.ParentId == mu.Id || cGroup.ParentId == mu.civilCode {
|
child := GbTreeMenu{
|
Id: cGroup.PublicID,
|
Name: cGroup.Name,
|
Type: "menu",
|
civilCode: cGroup.ChanPubID,
|
parentId: mu.Id,
|
}
|
|
recurseGbMenu(devPubId, &child, uArr, unitM, civilM, groupArr, cArr, gbcM, movedM, ncm)
|
if _, chk := ncm[child.Id]; chk {
|
child.Checked = true
|
ncm[mu.Id] = mu.Id
|
}
|
mu.Children = append(mu.Children, child)
|
}
|
}
|
|
//递归处理所有下级行政区域,以及区域下的摄像机
|
for _, u := range *uArr {
|
if u.ParentID == mu.Id {
|
child := GbTreeMenu{
|
Id: u.PublicID,
|
Name: u.Name,
|
Type: "menu",
|
civilCode: u.PublicID,
|
parentId: mu.Id,
|
}
|
|
recurseGbMenu(devPubId, &child, uArr, unitM, civilM, groupArr, cArr, gbcM, movedM, ncm)
|
if _, chk := ncm[child.Id]; chk {
|
child.Checked = true
|
ncm[mu.Id] = mu.Id
|
}
|
mu.Children = append(mu.Children, child)
|
}
|
}
|
|
//有些摄像机是挂在当前这一级平台下的
|
parentChecked := false
|
for _, c := range *cArr {
|
if c.DevPubID == devPubId {
|
if _, ok := movedM[c.PublicID]; !ok { //未处理过此摄像机
|
if c.ParentId != "" {
|
_, in := unitM[c.ParentId]
|
_, inUnitCiv := unitM[c.CivilCode]
|
_, inCivil := civilM[c.CivilCode]
|
_, inChanPub := civilM[c.ParentId]
|
//如果目录树中没有此摄像机的parentId,并且civilCode表中也没有,则挂到平台根目录下
|
if !in && !inCivil && !inUnitCiv && !inChanPub {
|
cu := GbTreeMenu{
|
Id: c.PublicID,
|
Name: c.Name,
|
Type: "camera",
|
Alive: c.Alive,
|
}
|
if _, ok := gbcM[cu.Id]; ok {
|
cu.Checked = true
|
parentChecked = true
|
ncm[cu.Id] = cu.Id
|
}
|
|
mu.Children = append(mu.Children, cu)
|
|
movedM[c.PublicID] = c.PublicID
|
} else {
|
if c.ParentId == mu.Id || c.CivilCode == mu.Id || c.CivilCode == mu.civilCode {
|
cu := GbTreeMenu{
|
Id: c.PublicID,
|
Name: c.Name,
|
Type: "camera",
|
Alive: c.Alive,
|
}
|
if _, ok := gbcM[cu.Id]; ok {
|
cu.Checked = true
|
parentChecked = true
|
ncm[cu.Id] = cu.Id
|
}
|
mu.Children = append(mu.Children, cu)
|
|
movedM[c.PublicID] = c.PublicID
|
}
|
}
|
} else {
|
//摄像机的parentId为空,则挂到根目录下
|
cu := GbTreeMenu{
|
Id: c.PublicID,
|
Name: c.Name,
|
Type: "camera",
|
Alive: c.Alive,
|
}
|
if _, ok := gbcM[cu.Id]; ok {
|
cu.Checked = true
|
parentChecked = true
|
ncm[cu.Id] = cu.Id
|
}
|
|
mu.Children = append(mu.Children, cu)
|
|
movedM[c.PublicID] = c.PublicID
|
}
|
}
|
}
|
}
|
mu.Checked = parentChecked
|
if parentChecked {
|
ncm[mu.Id] = mu.Id
|
}
|
}
|
|
//刷新后保存国标摄像机的配置,(目前的需求是国标摄像机配置路数不能超过500路)
|
func (s Gb28181Service) SaveGb28181CamTree(reqBody []GbTreeMenu) (bool, error) {
|
logger.Debug("SaveGb28181CamTree reqBody:", reqBody)
|
//首先检查待配置的摄像机数量是否超过限制路数
|
totalCount := 0
|
recurseGbMenuCount(reqBody, &totalCount)
|
limit := config.Server.GbCamCount
|
if limit <= 0 {
|
limit = 500
|
}
|
if totalCount > limit {
|
return false, errors.New("国标摄像机数量最高不能超过:" + strconv.Itoa(limit) + "路")
|
}
|
var vd gbModels.VssDev
|
var du gbModels.VssDomainUnit
|
var vc gbModels.VssChannel
|
devices, _ := vd.FindAll()
|
uM := du.FindAllMap()
|
gbcM := vc.FindAllMap() //VSSChannelTbl表中可能保存有目录结构,resType=2
|
for _, d := range devices {
|
if len(d.PublicID) > 13 && d.PublicID[10:13] == "132" { //是个摄像机
|
logger.Debug("SaveGb28181CamTree d:", d)
|
} else { //是个平台
|
if _, ok := uM[d.PublicID]; !ok {
|
logger.Debug("SaveGb28181CamTree not contain d.id=>", d.PublicID, " d.name=>", d.Name)
|
uM[d.PublicID] = gbModels.VssDomainUnit{
|
DevPubID: d.PublicID,
|
PublicID: d.PublicID,
|
Name: d.Name,
|
}
|
}
|
}
|
}
|
var gbcModel models.Camera
|
gbCams, _ := gbcModel.FindCamerasByType(models.TYPE_GB28181_CAMERA, "")
|
logger.Debug("GetGbCurrentTree len(gbCams):", len(gbCams))
|
camCache := make(map[string]models.Camera, 0)
|
for _, gc := range gbCams {
|
camCache[gc.Id] = gc
|
}
|
//1.摄像机所属的目录吸入area表中
|
//2.摄像机写入camera表中
|
//3.建立camera和area的关联关系
|
var err error
|
tx := models.GetDB().Begin()
|
defer func() {
|
if err != nil && tx != nil {
|
tx.Rollback()
|
logger.Debug("saveGb28181CamTree err:", err)
|
}
|
}()
|
//清除旧的数据
|
err = tx.Exec("delete from " + models.Camera{}.TableName() + " where type=1").Error
|
if err != nil {
|
return false, err
|
}
|
err = tx.Exec("delete from " + models.Area{}.TableName() + " where type=1").Error
|
if err != nil {
|
return false, err
|
}
|
err = tx.Exec("delete from " + models.CameraArea{}.TableName() + " where cameraId not in (select id from cameras)").Error
|
if err != nil {
|
return false, err
|
}
|
|
//写入新的国标摄像机
|
uIdMap := make(map[string]gbDevAndArea, 0)
|
if err = recurseInsertGbCamOrUnit(tx, reqBody, uM, gbcM, uIdMap, "0", camCache); err != nil {
|
logger.Debug("recurseInsertGbCamOrUnit err:", err)
|
return false, err
|
}
|
|
tx.Commit()
|
logger.Debug("SaveGb28181CamTree end!")
|
s.AddDbChangeMsg(protomsg.DbAction_Update)
|
return true, nil
|
}
|
|
func (s Gb28181Service) GetGb28181VssTable() VssTable {
|
var vd gbModels.VssDev
|
var du gbModels.VssDomainUnit
|
var vc gbModels.VssChannel
|
devices, _ := vd.FindAll()
|
uM := du.FindAllMap()
|
gbcM := vc.FindAllMap()
|
var gbcModel models.Camera
|
gbCams, _ := gbcModel.FindCamerasByType(models.TYPE_GB28181_CAMERA, "")
|
var vssTable VssTable
|
vssTable.Vd = devices
|
vssTable.Du = uM
|
vssTable.Vc = gbcM
|
vssTable.GbCam = gbCams
|
return vssTable
|
}
|
|
func recurseInsertGbCamOrUnit(tx *gorm.DB, menus []GbTreeMenu, uM map[string]gbModels.VssDomainUnit, cM map[string]gbModels.VssChannel, uIdMap map[string]gbDevAndArea, parentId string, cc map[string]models.Camera) error {
|
for _, u := range menus {
|
if u.Type == "menu" {
|
uIdMap[u.Id] = gbDevAndArea{
|
PublicId: u.Id,
|
ParentId: parentId,
|
}
|
gbArea := models.Area{
|
Id: u.Id,
|
Name: u.Name,
|
Parentid: parentId,
|
Type: models.TYPE_GB28181_TREE,
|
}
|
if _, ok := uIdMap[parentId]; !ok {
|
gbArea.Parentid = "0"
|
}
|
//递归得到parentids
|
parentIds := ""
|
GetPidsByRecursion(&uIdMap, gbArea.Parentid, &parentIds)
|
gbArea.Parentids = parentIds
|
|
if u.Name == "" {
|
gbArea.Name = u.Id
|
}
|
var areaE models.Area
|
rows, _ := areaE.SelectbyId(gbArea.Id)
|
logger.Debug("recurseInsertGbCamOrUnit areaE.SelectById rows:", rows)
|
if rows > 0 {
|
gbArea.Alias = areaE.Alias
|
}
|
if err := tx.Table("area").Create(&gbArea).Error; err != nil {
|
return err
|
}
|
|
if err := recurseInsertGbCamOrUnit(tx, u.Children, uM, cM, uIdMap, u.Id, cc); err != nil {
|
logger.Debug("recurseInsertGbCamOrUnit err:", err)
|
return err
|
}
|
} else {
|
if gbCam, ok := cM[u.Id]; ok { //摄像机
|
cameraE := models.Camera{
|
Id: gbCam.PublicID,
|
Name: gbCam.Name,
|
Ip: gbCam.IP,
|
Port: gbCam.Port,
|
Rtsp: gbCam.RealRtspUrl,
|
|
Addr: gbCam.Name,
|
Alias: gbCam.Name, //别名默认取国标摄像机的名称
|
Longitude: float32(gbCam.Longitude),
|
Latitude: float32(gbCam.Latitude),
|
Type: models.TYPE_GB28181_CAMERA, //国标摄像机
|
Floor: models.Default_Layer,
|
}
|
if stc, sin := cc[cameraE.Id]; sin {
|
cameraE.RunType = stc.RunType //保留之前配置的运行状态
|
cameraE.RunServerId = stc.RunServerId
|
} else {
|
cameraE.RunType = models.TYPE_RUNTYPE_VIDEO
|
}
|
if err := tx.Table("cameras").Create(&cameraE).Error; err != nil {
|
return err
|
}
|
|
//关联摄像机和目录树
|
if err := tx.Table("camera_area").Create(&models.CameraArea{Cameraid: gbCam.PublicID, Areaid: parentId}).Error; err != nil {
|
return err
|
}
|
}
|
}
|
}
|
return nil
|
}
|
|
//递归获取国标树的摄像机数量
|
func recurseGbMenuCount(menus []GbTreeMenu, count *int) {
|
if menus != nil && len(menus) > 0 {
|
for _, u := range menus {
|
if u.Type == "camera" {
|
*count++
|
} else {
|
recurseGbMenuCount(u.Children, count)
|
}
|
}
|
}
|
}
|
|
//刷新国标树和设备
|
func (s Gb28181Service) RefreshById(id string) (bool, []GbTreeMenu) {
|
if b, _ := s.UpdateGb28181Data(); b {
|
data := s.GetGbCurrentTree()
|
return true, data
|
}
|
|
return false, []GbTreeMenu{}
|
}
|
|
//刷新国标树和设备
|
func (s Gb28181Service) RefreshAllChannel() (bool, []GbTreeMenu) {
|
if b, _ := s.UpdateGb28181Data(); b {
|
data := s.GetGbCameras()
|
return true, data
|
}
|
|
return false, []GbTreeMenu{}
|
}
|
|
//删除国标
|
func (s Gb28181Service) Delete() bool {
|
var err error
|
tx := models.GetDB().Begin()
|
defer func() {
|
if err != nil && tx != nil {
|
tx.Rollback()
|
}
|
}()
|
if err = tx.Exec("delete from area where type=1").Error; err != nil {
|
return false
|
}
|
if err = tx.Exec("delete from cameras where type=1").Error; err != nil {
|
return false
|
}
|
if err = tx.Exec("delete from camera_area where areaId in (select id from area where type=1)").Error; err != nil {
|
return false
|
}
|
tx.Commit()
|
s.AddDbChangeMsg(protomsg.DbAction_Update)
|
return false
|
}
|
|
//摄像机表有更新
|
func (s Gb28181Service) AddDbChangeMsg(action protomsg.DbAction) {
|
dbMsg := protomsg.DbChangeMessage{
|
Table: protomsg.TableChanged_T_Camera,
|
Action: action,
|
}
|
pb, _ := json.Marshal(dbMsg)
|
s.bk.Publish("gb28181-service", pb)
|
|
//cameraPolygon有更新
|
pMsg := protomsg.DbChangeMessage{
|
Table: protomsg.TableChanged_T_CameraPolygon,
|
Action: action,
|
}
|
ppm, _ := json.Marshal(pMsg)
|
s.bk.Publish("gb28181-service", ppm)
|
}
|