调整告警图片展示
This commit is contained in:
parent
83a50fa245
commit
011694d7a7
2
.gitignore
vendored
2
.gitignore
vendored
@ -42,7 +42,7 @@ tables/
|
||||
*.pprof
|
||||
*.test
|
||||
snap/*
|
||||
|
||||
*buf264/*
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
# 连续分析帧数(2-64), 默认为10, 最大为 64
|
||||
FrmNum = 10
|
||||
# 是否使用深度学习版本, 默认使用深度学习版本
|
||||
IsDeepLearn = false
|
||||
IsDeepLearn = true
|
||||
|
||||
[VqdLgtDark]
|
||||
# 默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6
|
||||
|
||||
@ -111,69 +111,69 @@ type VqdConfig struct {
|
||||
|
||||
// 亮度检测
|
||||
type VqdLgtDark struct {
|
||||
DarkThr float64 `json:"dark_thr" comment:"默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6"`
|
||||
LgtThr float64 `json:"lgt_thr" comment:"默认 0.1, 取值范围: 0~1, 建议范围: 0.1~0.5"`
|
||||
LgtDarkAbnNumRatio float64 `json:"lgt_dark_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
DarkThr float32 `json:"dark_thr" comment:"默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6"`
|
||||
LgtThr float32 `json:"lgt_thr" comment:"默认 0.1, 取值范围: 0~1, 建议范围: 0.1~0.5"`
|
||||
LgtDarkAbnNumRatio float32 `json:"lgt_dark_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
}
|
||||
|
||||
// 蓝屏检查
|
||||
type VqdBlue struct {
|
||||
BlueThr float64 `json:"blue_thr" comment:"默认为 0.6, 取值范围: 0~1, 建议范围 0.4~0.9"`
|
||||
BlueAbnNumRatio float64 `json:"blue_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
BlueThr float32 `json:"blue_thr" comment:"默认为 0.6, 取值范围: 0~1, 建议范围 0.4~0.9"`
|
||||
BlueAbnNumRatio float32 `json:"blue_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
}
|
||||
|
||||
// 清晰度检查
|
||||
type VqdClarity struct {
|
||||
ClarityThr float64 `json:"clarity_thr" comment:"默认为0.4, 取值范围: 0~1, 建议范围: 0.3~0.99"`
|
||||
ClarityAbnNumRatio float64 `json:"clarity_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
ClarityThr float32 `json:"clarity_thr" comment:"默认为0.4, 取值范围: 0~1, 建议范围: 0.3~0.99"`
|
||||
ClarityAbnNumRatio float32 `json:"clarity_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
}
|
||||
|
||||
// 抖动检查
|
||||
type VqdShark struct {
|
||||
SharkThr float64 `json:"shark_thr" comment:"默认为 0.2, 取值范围: 0~1, 建议范围: 0.1~0.8"`
|
||||
SharkAbnNumRatio float64 `json:"shark_abn_num_ratio" comment:"默认为0.2, 取值范围: 0~1, 建议范围: 0.1~0.6"`
|
||||
SharkThr float32 `json:"shark_thr" comment:"默认为 0.2, 取值范围: 0~1, 建议范围: 0.1~0.8"`
|
||||
SharkAbnNumRatio float32 `json:"shark_abn_num_ratio" comment:"默认为0.2, 取值范围: 0~1, 建议范围: 0.1~0.6"`
|
||||
}
|
||||
|
||||
// 冻结检测
|
||||
type VqdFreeze struct {
|
||||
FreezeThr float64 `json:"freeze_thr" comment:"默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6"`
|
||||
FreezeAbnNumRatio float64 `json:"freeze_abn_num_ratio" comment:"默认为0.99, 取值范围: 0.8~1, 建议范围: 0.95~1"`
|
||||
FreezeThr float32 `json:"freeze_thr" comment:"默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6"`
|
||||
FreezeAbnNumRatio float32 `json:"freeze_abn_num_ratio" comment:"默认为0.99, 取值范围: 0.8~1, 建议范围: 0.95~1"`
|
||||
}
|
||||
|
||||
// 偏色检测
|
||||
type VqdColor struct {
|
||||
ColorThr float64 `json:"color_thr" comment:"默认为0.18, 取值范围: 0~1, 建议范围: 0.1~0.5"`
|
||||
ColorAbnNumRatio float64 `json:"color_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9"`
|
||||
ColorThr float32 `json:"color_thr" comment:"默认为0.18, 取值范围: 0~1, 建议范围: 0.1~0.5"`
|
||||
ColorAbnNumRatio float32 `json:"color_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9"`
|
||||
}
|
||||
|
||||
// 遮挡检测
|
||||
type VqdOcclusion struct {
|
||||
OcclusionThr float64 `json:"occlusion_thr" comment:"默认为0.1, 取值范围: 0~1, 建议范围: 0.05~0.5"`
|
||||
OcclusionAbnNumRatio float64 `json:"occlusion_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9"`
|
||||
OcclusionThr float32 `json:"occlusion_thr" comment:"默认为0.1, 取值范围: 0~1, 建议范围: 0.05~0.5"`
|
||||
OcclusionAbnNumRatio float32 `json:"occlusion_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9"`
|
||||
}
|
||||
|
||||
// 噪声检测
|
||||
type VqdNoise struct {
|
||||
NoiseThr float64 `json:"noise_thr" comment:"默认为 0.3, 取值范围: 0~1, 建议范围: 0.2~0.8"`
|
||||
NoiseAbnNumRatio float64 `json:"noise_abn_num_ratio" comment:"默认为0.6, 取值范围: 0~1, 建议范围: 0.3~0.9"`
|
||||
NoiseThr float32 `json:"noise_thr" comment:"默认为 0.3, 取值范围: 0~1, 建议范围: 0.2~0.8"`
|
||||
NoiseAbnNumRatio float32 `json:"noise_abn_num_ratio" comment:"默认为0.6, 取值范围: 0~1, 建议范围: 0.3~0.9"`
|
||||
}
|
||||
|
||||
// 对比度检测
|
||||
type VqdContrast struct {
|
||||
CtraLowThr float64 `json:"ctra_low_thr" comment:"默认为 0.2, 取值范围: 0~1, 建议范围: 0.1~0.3"`
|
||||
CtraHighThr float64 `json:"ctra_high_thr" comment:"默认为 0.8, 取值范围: 0~1, 建议范围: 0.7~0.9"`
|
||||
CtraAbnNumRatio float64 `json:"ctra_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9"`
|
||||
CtraLowThr float32 `json:"ctra_low_thr" comment:"默认为 0.2, 取值范围: 0~1, 建议范围: 0.1~0.3"`
|
||||
CtraHighThr float32 `json:"ctra_high_thr" comment:"默认为 0.8, 取值范围: 0~1, 建议范围: 0.7~0.9"`
|
||||
CtraAbnNumRatio float32 `json:"ctra_abn_num_ratio" comment:"默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9"`
|
||||
}
|
||||
|
||||
// 马赛克检测
|
||||
type VqdMosaic struct {
|
||||
MosaicThr float64 `json:"mosaic_thr" comment:"默认为 0.1 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
MosaicAbnNumRatio float64 `json:"mosaic_abn_num_ratio" comment:"默认为0.5,取值范围: 0~1, 建议范围: 0.3"`
|
||||
MosaicThr float32 `json:"mosaic_thr" comment:"默认为 0.1 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
MosaicAbnNumRatio float32 `json:"mosaic_abn_num_ratio" comment:"默认为0.5,取值范围: 0~1, 建议范围: 0.3"`
|
||||
}
|
||||
|
||||
// 花屏检测
|
||||
type VqdFlower struct {
|
||||
FlowerThr float64 `json:"flower_thr" comment:"默认为 0.3 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
FlowerAbnNumRatio float64 `json:"flower_abn_num_ratio" comment:"默认为0.6, 取值范围: 0~1, 建议范围: 0.3"`
|
||||
MosaicThr float64 `json:"mosaic_thr" comment:"默认为 0.3 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
FlowerThr float32 `json:"flower_thr" comment:"默认为 0.3 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
FlowerAbnNumRatio float32 `json:"flower_abn_num_ratio" comment:"默认为0.6, 取值范围: 0~1, 建议范围: 0.3"`
|
||||
MosaicThr float32 `json:"mosaic_thr" comment:"默认为 0.3 取值范围: 0~1, 建议范围: 0.1~0.9"`
|
||||
}
|
||||
|
||||
@ -52,8 +52,8 @@ func NewCore(cfg *conf.Bootstrap) *Core {
|
||||
}
|
||||
sdk.AddResponseHandler("stop", core.stop)
|
||||
sdk.AddResponseHandler("ping", core.ping)
|
||||
|
||||
// 这部分都是收到响应后的回调
|
||||
sdk.AddResponseHandler("play", core.playRespH)
|
||||
sdk.AddResponseHandler("findDevices", core.findDevicesRespH)
|
||||
sdk.AddResponseHandler("findChannels", core.findChannelsRespH)
|
||||
sdk.AddResponseHandler("getBaseConfig", core.getBaseConfigRespH)
|
||||
@ -102,6 +102,11 @@ func (c Core) findTalkUrlRespH(requestID string, args json.RawMessage) (interfac
|
||||
slog.Debug("Received 'findTalkUrl' from host", "request_id", requestID, "args", args)
|
||||
return nil, nil
|
||||
}
|
||||
func (c Core) playRespH(requestID string, args json.RawMessage) (interface{}, error) {
|
||||
slog.Debug("Received 'play' from host", "request_id", requestID, "args", args)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c Core) iframeDataRespH(requestID string, args json.RawMessage) (interface{}, error) {
|
||||
slog.Debug("Received 'iframeData' from host", "request_id", requestID, "args", args)
|
||||
return nil, nil
|
||||
|
||||
24
internal/core/host/play.go
Normal file
24
internal/core/host/play.go
Normal file
@ -0,0 +1,24 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func (c Core) Play(ctx context.Context, in *PlayInput) (*PlayOutput, error) {
|
||||
marshal, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := c.Plugin.CallHost("play", marshal)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := PlayOutput{}
|
||||
if err = json.Unmarshal(result, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
40
internal/core/host/play.param.go
Normal file
40
internal/core/host/play.param.go
Normal file
@ -0,0 +1,40 @@
|
||||
package host
|
||||
|
||||
type PlayInput struct {
|
||||
ChannelID string `json:"channel_id"`
|
||||
Stream string `json:"stream"` // 主子码流 MAIN/SUB
|
||||
Protocol string `json:"protocol"` // hls/webrtc/flv 等播放协议
|
||||
Network string `json:"network"` // LAN:内网;WAN:公网(rtsp/rtmp 返回的地址)
|
||||
ActiveSecond int `json:"active_second"` // 流活跃时间
|
||||
TimeS int `form:"time_s" json:"time_s"` // 秒
|
||||
|
||||
IsRecord bool `json:"-"` // 是否由录像拉起
|
||||
Reason string `json:"-"` // 调用原因
|
||||
Domain string `json:"-"` // 域名
|
||||
RequestHost string `json:"-"` // 请求 url 上的 host,用于自适应返回播放地址
|
||||
Host string `json:"-"`
|
||||
|
||||
// 在未来的版本中,将废弃
|
||||
IsHTTPS bool `json:"-"` // 是否是 https 请求
|
||||
Auth bool `json:"-"` // 是否是 auth 请求
|
||||
}
|
||||
type PlayOutput struct {
|
||||
ChannelID string `json:"channel_id"`
|
||||
StreamID string `json:"stream_id"`
|
||||
Address map[string]string `json:"address"`
|
||||
Routes []Route `json:"routes"` // 多线路
|
||||
Img []byte `json:"img"`
|
||||
ImgType string `json:"img_type"`
|
||||
ImgCreateAt int64 `json:"img_created_at"`
|
||||
}
|
||||
type Route struct {
|
||||
ID int `json:"id"`
|
||||
Label string `json:"label"`
|
||||
|
||||
HTTPFLV string `json:"http_flv"`
|
||||
WSFLV string `json:"ws_flv"`
|
||||
HLS string `json:"hls"`
|
||||
RTMP string `json:"rtmp"`
|
||||
RTSP string `json:"rtsp"`
|
||||
WebRTC string `json:"webrtc"`
|
||||
}
|
||||
@ -60,9 +60,9 @@ func (i *VqdConfig) Scan(input interface{}) error {
|
||||
// 亮度检测
|
||||
type VqdLgtDark struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
DarkThr float64 `gorm:"column:dark_thr;notNull;default:0;comment:过暗阈值" json:"dark_thr"` // 默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6
|
||||
LgtThr float64 `gorm:"column:lgt_thr;notNull;default:0;comment:过亮阈值" json:"lgt_thr"` // 默认 0.1, 取值范围: 0~1, 建议范围: 0.1~0.5
|
||||
LgtDarkAbnNumRatio float64 `gorm:"column:lgt_dark_abn_num_ratio;notNull;default:0;comment:偏暗或者偏亮次数比例" json:"lgt_dark_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
DarkThr float32 `gorm:"column:dark_thr;notNull;default:0;comment:过暗阈值" json:"dark_thr"` // 默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6
|
||||
LgtThr float32 `gorm:"column:lgt_thr;notNull;default:0;comment:过亮阈值" json:"lgt_thr"` // 默认 0.1, 取值范围: 0~1, 建议范围: 0.1~0.5
|
||||
LgtDarkAbnNumRatio float32 `gorm:"column:lgt_dark_abn_num_ratio;notNull;default:0;comment:偏暗或者偏亮次数比例" json:"lgt_dark_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
}
|
||||
|
||||
func (a VqdLgtDark) Value() (driver.Value, error) {
|
||||
@ -75,8 +75,8 @@ func (i *VqdLgtDark) Scan(input interface{}) error {
|
||||
// 蓝屏检查
|
||||
type VqdBlue struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
BlueThr float64 `gorm:"column:blue_thr;notNull;default:0;comment:蓝屏判断阈值" json:"blue_thr"` // 默认为 0.6, 取值范围: 0~1, 建议范围 0.4~0.9
|
||||
BlueAbnNumRatio float64 `gorm:"column:blue_abn_num_ratio;notNull;default:0;comment:蓝屏次数比例" json:"blue_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
BlueThr float32 `gorm:"column:blue_thr;notNull;default:0;comment:蓝屏判断阈值" json:"blue_thr"` // 默认为 0.6, 取值范围: 0~1, 建议范围 0.4~0.9
|
||||
BlueAbnNumRatio float32 `gorm:"column:blue_abn_num_ratio;notNull;default:0;comment:蓝屏次数比例" json:"blue_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
}
|
||||
|
||||
func (a VqdBlue) Value() (driver.Value, error) {
|
||||
@ -89,8 +89,8 @@ func (i *VqdBlue) Scan(input interface{}) error {
|
||||
// 清晰度检查
|
||||
type VqdClarity struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
ClarityThr float64 `gorm:"column:clarity_thr;notNull;default:0;comment:清晰度判断阈值" json:"clarity_thr"` // 默认为0.4, 取值范围: 0~1, 建议范围: 0.3~0.99
|
||||
ClarityAbnNumRatio float64 `gorm:"column:clarity_abn_num_ratio;notNull;default:0;comment:清晰度异常次数比例" json:"clarity_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
ClarityThr float32 `gorm:"column:clarity_thr;notNull;default:0;comment:清晰度判断阈值" json:"clarity_thr"` // 默认为0.4, 取值范围: 0~1, 建议范围: 0.3~0.99
|
||||
ClarityAbnNumRatio float32 `gorm:"column:clarity_abn_num_ratio;notNull;default:0;comment:清晰度异常次数比例" json:"clarity_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
}
|
||||
|
||||
func (a VqdClarity) Value() (driver.Value, error) {
|
||||
@ -103,8 +103,8 @@ func (i *VqdClarity) Scan(input interface{}) error {
|
||||
// 抖动检查
|
||||
type VqdShark struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
SharkThr float64 `gorm:"column:shark_thr;notNull;default:0;comment:抖动阈值参数" json:"shark_thr"` // 默认为 0.2, 取值范围: 0~1, 建议范围: 0.1~0.8
|
||||
SharkAbnNumRatio float64 `gorm:"column:shark_abn_num_ratio;notNull;default:0;comment:出现抖动次数的比例" json:"shark_abn_num_ratio"` // 默认为0.2, 取值范围: 0~1, 建议范围: 0.1~0.6
|
||||
SharkThr float32 `gorm:"column:shark_thr;notNull;default:0;comment:抖动阈值参数" json:"shark_thr"` // 默认为 0.2, 取值范围: 0~1, 建议范围: 0.1~0.8
|
||||
SharkAbnNumRatio float32 `gorm:"column:shark_abn_num_ratio;notNull;default:0;comment:出现抖动次数的比例" json:"shark_abn_num_ratio"` // 默认为0.2, 取值范围: 0~1, 建议范围: 0.1~0.6
|
||||
}
|
||||
|
||||
func (a VqdShark) Value() (driver.Value, error) {
|
||||
@ -117,8 +117,8 @@ func (i *VqdShark) Scan(input interface{}) error {
|
||||
// 冻结检测
|
||||
type VqdFreeze struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
FreezeThr float64 `gorm:"column:freeze_thr;notNull;default:0;comment:冻结阈值参数" json:"freeze_thr"` // 默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6
|
||||
FreezeAbnNumRatio float64 `gorm:"column:freeze_abn_num_ratio;notNull;default:0;comment:冻结帧数占得比例" json:"freeze_abn_num_ratio"` // 默认为0.99, 取值范围: 0.8~1, 建议范围: 0.95~1
|
||||
FreezeThr float32 `gorm:"column:freeze_thr;notNull;default:0;comment:冻结阈值参数" json:"freeze_thr"` // 默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6
|
||||
FreezeAbnNumRatio float32 `gorm:"column:freeze_abn_num_ratio;notNull;default:0;comment:冻结帧数占得比例" json:"freeze_abn_num_ratio"` // 默认为0.99, 取值范围: 0.8~1, 建议范围: 0.95~1
|
||||
}
|
||||
|
||||
func (a VqdFreeze) Value() (driver.Value, error) {
|
||||
@ -131,8 +131,8 @@ func (i *VqdFreeze) Scan(input interface{}) error {
|
||||
// 偏色检测
|
||||
type VqdColor struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
ColorThr float64 `gorm:"column:color_thr;notNull;default:0;comment:偏色判断值" json:"color_thr"` // 默认为0.18, 取值范围: 0~1, 建议范围: 0.1~0.5
|
||||
ColorAbnNumRatio float64 `gorm:"column:color_abn_num_ratio;notNull;default:0;comment:偏色次数比例" json:"color_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9
|
||||
ColorThr float32 `gorm:"column:color_thr;notNull;default:0;comment:偏色判断值" json:"color_thr"` // 默认为0.18, 取值范围: 0~1, 建议范围: 0.1~0.5
|
||||
ColorAbnNumRatio float32 `gorm:"column:color_abn_num_ratio;notNull;default:0;comment:偏色次数比例" json:"color_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9
|
||||
}
|
||||
|
||||
func (a VqdColor) Value() (driver.Value, error) {
|
||||
@ -145,8 +145,8 @@ func (i *VqdColor) Scan(input interface{}) error {
|
||||
// 遮挡检测
|
||||
type VqdOcclusion struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
OcclusionThr float64 `gorm:"column:occlusion_thr;notNull;default:0;comment:遮挡判断阈值" json:"occlusion_thr"` // 默认为0.1, 取值范围: 0~1, 建议范围: 0.05~0.5
|
||||
OcclusionAbnNumRatio float64 `gorm:"column:occlusion_abn_num_ratio;notNull;default:0;comment:遮挡次数比例" json:"occlusion_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9
|
||||
OcclusionThr float32 `gorm:"column:occlusion_thr;notNull;default:0;comment:遮挡判断阈值" json:"occlusion_thr"` // 默认为0.1, 取值范围: 0~1, 建议范围: 0.05~0.5
|
||||
OcclusionAbnNumRatio float32 `gorm:"column:occlusion_abn_num_ratio;notNull;default:0;comment:遮挡次数比例" json:"occlusion_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9
|
||||
}
|
||||
|
||||
func (a VqdOcclusion) Value() (driver.Value, error) {
|
||||
@ -159,8 +159,8 @@ func (i *VqdOcclusion) Scan(input interface{}) error {
|
||||
// 噪声检测
|
||||
type VqdNoise struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
NoiseThr float64 `gorm:"column:noise_thr;notNull;default:0;comment:噪声判断阈值" json:"noise_thr"` // 默认为 0.3, 取值范围: 0~1, 建议范围: 0.2~0.8
|
||||
NoiseAbnNumRatio float64 `gorm:"column:noise_abn_num_ratio;notNull;default:0;comment:噪声次数比例" json:"noise_abn_num_ratio"` // 默认为0.6, 取值范围: 0~1, 建议范围: 0.3~0.9
|
||||
NoiseThr float32 `gorm:"column:noise_thr;notNull;default:0;comment:噪声判断阈值" json:"noise_thr"` // 默认为 0.3, 取值范围: 0~1, 建议范围: 0.2~0.8
|
||||
NoiseAbnNumRatio float32 `gorm:"column:noise_abn_num_ratio;notNull;default:0;comment:噪声次数比例" json:"noise_abn_num_ratio"` // 默认为0.6, 取值范围: 0~1, 建议范围: 0.3~0.9
|
||||
}
|
||||
|
||||
func (a VqdNoise) Value() (driver.Value, error) {
|
||||
@ -173,9 +173,9 @@ func (i *VqdNoise) Scan(input interface{}) error {
|
||||
// 对比度检测
|
||||
type VqdContrast struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
CtraLowThr float64 `gorm:"column:ctra_low_thr;notNull;default:0;comment:低对比度判断阈值" json:"ctra_low_thr"` // 默认为 0.2, 取值范围: 0~1, 建议范围: 0.1~0.3
|
||||
CtraHighThr float64 `gorm:"column:ctra_high_thr;notNull;default:0;comment:高对比度判断阈值" json:"ctra_high_thr"` // 默认为 0.8, 取值范围: 0~1, 建议范围: 0.7~0.9
|
||||
CtraAbnNumRatio float64 `gorm:"column:ctra_abn_num_ratio;notNull;default:0;comment:对比度异常次数比例" json:"ctra_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9
|
||||
CtraLowThr float32 `gorm:"column:ctra_low_thr;notNull;default:0;comment:低对比度判断阈值" json:"ctra_low_thr"` // 默认为 0.2, 取值范围: 0~1, 建议范围: 0.1~0.3
|
||||
CtraHighThr float32 `gorm:"column:ctra_high_thr;notNull;default:0;comment:高对比度判断阈值" json:"ctra_high_thr"` // 默认为 0.8, 取值范围: 0~1, 建议范围: 0.7~0.9
|
||||
CtraAbnNumRatio float32 `gorm:"column:ctra_abn_num_ratio;notNull;default:0;comment:对比度异常次数比例" json:"ctra_abn_num_ratio"` // 默认为0.5, 取值范围: 0~1, 建议范围: 0.3~0.9
|
||||
}
|
||||
|
||||
func (a VqdContrast) Value() (driver.Value, error) {
|
||||
@ -188,8 +188,8 @@ func (i *VqdContrast) Scan(input interface{}) error {
|
||||
// 马赛克检测
|
||||
type VqdMosaic struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
MosaicThr float64 `gorm:"column:mosaic_thr;notNull;default:0;comment:马赛克阈值参数" json:"mosaic_thr"` // 默认为 0.1 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
MosaicAbnNumRatio float64 `gorm:"column:mosaic_abn_num_ratio;notNull;default:0;comment:马赛克次数比例" json:"mosaic_abn_num_ratio"` // 默认为0.5,取值范围: 0~1, 建议范围: 0.3
|
||||
MosaicThr float32 `gorm:"column:mosaic_thr;notNull;default:0;comment:马赛克阈值参数" json:"mosaic_thr"` // 默认为 0.1 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
MosaicAbnNumRatio float32 `gorm:"column:mosaic_abn_num_ratio;notNull;default:0;comment:马赛克次数比例" json:"mosaic_abn_num_ratio"` // 默认为0.5,取值范围: 0~1, 建议范围: 0.3
|
||||
}
|
||||
|
||||
func (a VqdMosaic) Value() (driver.Value, error) {
|
||||
@ -202,9 +202,9 @@ func (i *VqdMosaic) Scan(input interface{}) error {
|
||||
// 花屏检测
|
||||
type VqdFlower struct {
|
||||
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
|
||||
FlowerThr float64 `gorm:"column:flower_thr;notNull;default:0;comment:花屏阈值参数" json:"flower_thr"` // 默认为 0.3 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
FlowerAbnNumRatio float64 `gorm:"column:flower_abn_num_ratio;notNull;default:0;comment:花屏次数比例" json:"flower_abn_num_ratio"` // 默认为0.6, 取值范围: 0~1, 建议范围: 0.3
|
||||
MosaicThr float64 `gorm:"column:mosaic_thr;notNull;default:0;comment:阈值" json:"mosaic_thr"` // 默认为 0.3 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
FlowerThr float32 `gorm:"column:flower_thr;notNull;default:0;comment:花屏阈值参数" json:"flower_thr"` // 默认为 0.3 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
FlowerAbnNumRatio float32 `gorm:"column:flower_abn_num_ratio;notNull;default:0;comment:花屏次数比例" json:"flower_abn_num_ratio"` // 默认为0.6, 取值范围: 0~1, 建议范围: 0.3
|
||||
MosaicThr float32 `gorm:"column:mosaic_thr;notNull;default:0;comment:阈值" json:"mosaic_thr"` // 默认为 0.3 取值范围: 0~1, 建议范围: 0.1~0.9
|
||||
}
|
||||
|
||||
func (a VqdFlower) Value() (driver.Value, error) {
|
||||
@ -252,18 +252,50 @@ func (*VqdTimeTemplate) TableName() string {
|
||||
return "vqd_time_template"
|
||||
}
|
||||
|
||||
type Abnormal struct {
|
||||
Value float32 `json:"value"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Abnormals []Abnormal
|
||||
|
||||
func (a Abnormals) Value() (driver.Value, error) {
|
||||
return json.Marshal(a)
|
||||
}
|
||||
func (i *Abnormals) Scan(input interface{}) error {
|
||||
return orm.JsonUnmarshal(input, i)
|
||||
}
|
||||
|
||||
type DefaultValue struct {
|
||||
Thr1 float32 `json:"thr1"`
|
||||
Name1 string `json:"name1"`
|
||||
Thr2 float32 `json:"thr2"`
|
||||
Name2 string `json:"name2"`
|
||||
Ratio float32 `json:"ratio"` //比例
|
||||
}
|
||||
type DefaultValues []DefaultValue
|
||||
|
||||
func (a DefaultValues) Value() (driver.Value, error) {
|
||||
return json.Marshal(a)
|
||||
}
|
||||
func (i *DefaultValues) Scan(input interface{}) error {
|
||||
return orm.JsonUnmarshal(input, i)
|
||||
}
|
||||
|
||||
type VqdAlarm struct {
|
||||
orm.Model
|
||||
AlarmName string `gorm:"column:alarm_name;notNull;default:'';comment:告警名称" json:"alarm_name"` // 告警名称
|
||||
AlarmValue string `gorm:"column:alarm_value;notNull;default:'';comment:告警参数" json:"alarm_value"` // 告警参数
|
||||
ChannelID string `gorm:"column:channel_id;notNull;default:'';comment:关联通道" json:"channel_id"` // 关联通道
|
||||
ChannelName string `gorm:"column:channel_name;notNull;default:'';comment:关联通道名称" json:"channel_name"` // 关联通道名称
|
||||
TaskTemplateID int64 `gorm:"column:task_template_id;notNull;default:0;comment:关联模板" json:"task_template_id"` //关联模板
|
||||
TaskTemplateName string `gorm:"column:task_template_name;notNull;default:0;comment:关联模板名称" json:"task_template_name"` //关联模板名称
|
||||
TaskID int64 `gorm:"column:task_id;notNull;default:0;comment:关联任务" json:"task_id"` // 关联任务名称
|
||||
TaskName string `gorm:"column:task_name;notNull;default:'';comment:关联任务名称" json:"task_name"` // 任务名称
|
||||
FilePath string `gorm:"column:file_path;notNull;default:'';comment:文件路径" json:"file_path"` // 文件路径
|
||||
|
||||
IsDeep bool `gorm:"column:is_deep;notNull;default:FALSE;comment:启用" json:"is_deep"`
|
||||
ChannelID string `gorm:"column:channel_id;notNull;default:'';comment:关联通道" json:"channel_id"` // 关联通道
|
||||
ChannelName string `gorm:"column:channel_name;notNull;default:'';comment:关联通道名称" json:"channel_name"` // 关联通道名称
|
||||
PlanID int64 `gorm:"column:plan_id;notNull;default:0;comment:关联计划" json:"plan_id"` // 关联计划
|
||||
PlanName string `gorm:"column:plan_name;notNull;default:0;comment:关联计划名称" json:"plan_name"` // 关联计划名称
|
||||
TaskTemplateID int64 `gorm:"column:task_template_id;notNull;default:0;comment:关联模板" json:"task_template_id"` // 关联模板
|
||||
TaskTemplateName string `gorm:"column:task_template_name;notNull;default:0;comment:关联模板名称" json:"task_template_name"` // 关联模板名称
|
||||
TaskID int64 `gorm:"column:task_id;notNull;default:0;comment:关联任务" json:"task_id"` // 关联任务名称
|
||||
TaskName string `gorm:"column:task_name;notNull;default:'';comment:关联任务名称" json:"task_name"` // 任务名称
|
||||
FilePath string `gorm:"column:file_path;notNull;default:'';comment:文件路径" json:"file_path"` // 文件路径
|
||||
Abnormals Abnormals `gorm:"column:abnormals;type:jsonb;notNull;default:'{}';comment:告警异常列表" json:"abnormals"` // 告警异常列表
|
||||
DefaultValues DefaultValues `gorm:"column:default_values;type:jsonb;notNull;default:'{}';comment:设置的默认阈值" json:"default_values"` // 设置的默认阈值
|
||||
}
|
||||
|
||||
// TableName database table name
|
||||
|
||||
@ -34,9 +34,9 @@ func (c Core) FindVqdAlarmAll() ([]*VqdAlarm, int64, error) {
|
||||
// FindVqdAlarm Paginated search
|
||||
func (c Core) FindVqdAlarm(ctx context.Context, in *FindVqdAlarmInput) ([]*VqdAlarm, int64, error) {
|
||||
items := make([]*VqdAlarm, 0)
|
||||
if in.AlarmName != "" {
|
||||
if in.Name != "" {
|
||||
query := orm.NewQuery(8).
|
||||
Where("audio_name like ? OR channel_id like ? OR channel_name like ?", "%"+in.AlarmName+"%", "%"+in.AlarmName+"%", "%"+in.AlarmName+"%").OrderBy("created_at DESC")
|
||||
Where("channel_name like ? OR channel_id like ? OR channel_name like ? OR plan_name like ? OR task_template_name like ? OR task_name like ? ", "%"+in.Name+"%", "%"+in.Name+"%", "%"+in.Name+"%", "%"+in.Name+"%", "%"+in.Name+"%", "%"+in.Name+"%").OrderBy("created_at DESC")
|
||||
total, err := c.store.VqdAlarm().Find(ctx, &items, in, query.Encode()...)
|
||||
if err != nil {
|
||||
return nil, 0, reason.ErrDB.Withf(`Find err[%s]`, err.Error())
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
type FindVqdAlarmInput struct {
|
||||
web.PagerFilter
|
||||
AlarmName string `form:"alarm_name"` // 名称
|
||||
Name string `form:"name"` // 名称
|
||||
}
|
||||
|
||||
type EditVqdAlarmInput struct {
|
||||
@ -15,15 +15,18 @@ type EditVqdAlarmInput struct {
|
||||
}
|
||||
|
||||
type AddVqdAlarmInput struct {
|
||||
AlarmName string `json:"alarm_name"` // 告警名称
|
||||
AlarmValue string `json:"alarm_value"` // 告警参数
|
||||
ChannelID string `json:"channel_id"` // 关联通道
|
||||
ChannelName string `json:"channel_name"` // 关联通道名称
|
||||
TaskTemplateID int64 `json:"task_template_id"` // 关联模板
|
||||
TaskTemplateName string `json:"task_template_name"` // 关联模板名称
|
||||
TaskID int64 `json:"task_id"` // 关联任务名称
|
||||
TaskName string `json:"task_name"` // 任务名称
|
||||
FilePath string `json:"file_path"` // 文件路径
|
||||
PlanID int64 `json:"plan_id"` // 关联计划
|
||||
PlanName string `json:"plan_name"` // 关联计划名称
|
||||
ChannelID string `json:"channel_id"` // 关联通道
|
||||
ChannelName string `json:"channel_name"` // 关联通道名称
|
||||
TaskTemplateID int64 `json:"task_template_id"` // 关联模板
|
||||
TaskTemplateName string `json:"task_template_name"` // 关联模板名称
|
||||
TaskID int64 `json:"task_id"` // 关联任务名称
|
||||
TaskName string `json:"task_name"` // 任务名称
|
||||
FilePath string `json:"file_path"` // 文件路径
|
||||
IsDeep bool `json:"is_deep"`
|
||||
Abnormals Abnormals `json:"abnormals"` // 告警异常列表
|
||||
DefaultValues DefaultValues `json:"default_values"` // 设置的默认阈值
|
||||
}
|
||||
type DelVqdAlarmInput struct {
|
||||
IDs []int `json:"ids"` // id
|
||||
|
||||
@ -31,6 +31,34 @@ func (c Core) FindVqdTaskAll() ([]*VqdTask, int64, error) {
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
// FindVqdTemplateID Paginated search
|
||||
func (c Core) FindVqdPlanID(ctx context.Context, id int) ([]*VqdTask, int64, error) {
|
||||
items := make([]*VqdTask, 0)
|
||||
in := &FindVqdTaskInput{}
|
||||
in.Size = 99999
|
||||
in.Page = 1
|
||||
query := orm.NewQuery(2).Where("time_template_id = ?", id)
|
||||
total, err := c.store.VqdTask().Find(ctx, &items, in, query.Encode()...)
|
||||
if err != nil {
|
||||
return nil, 0, reason.ErrDB.Withf(`Find err[%s]`, err.Error())
|
||||
}
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
// FindVqdTemplateID Paginated search
|
||||
func (c Core) FindVqdTemplateID(ctx context.Context, id int) ([]*VqdTask, int64, error) {
|
||||
items := make([]*VqdTask, 0)
|
||||
in := &FindVqdTaskInput{}
|
||||
in.Size = 99999
|
||||
in.Page = 1
|
||||
query := orm.NewQuery(2).Where("task_template_id = ?", id)
|
||||
total, err := c.store.VqdTask().Find(ctx, &items, in, query.Encode()...)
|
||||
if err != nil {
|
||||
return nil, 0, reason.ErrDB.Withf(`Find err[%s]`, err.Error())
|
||||
}
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
// FindVqdTask Paginated search
|
||||
func (c Core) FindVqdTask(ctx context.Context, in *FindVqdTaskInput) ([]*VqdTask, int64, error) {
|
||||
items := make([]*VqdTask, 0)
|
||||
@ -64,6 +92,18 @@ func (c Core) FindVqdTaskTemplateID(ctx context.Context, id int) (*VqdTask, erro
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// FindVqdTaskPlanID Query a single object
|
||||
func (c Core) FindVqdTaskPlanID(ctx context.Context, id int) (*VqdTask, error) {
|
||||
var out VqdTask
|
||||
if err := c.store.VqdTask().Get(ctx, &out, orm.Where("time_template_id=?", id)); err != nil {
|
||||
if orm.IsErrRecordNotFound(err) {
|
||||
return nil, reason.ErrNotFound.Withf(`Get err[%s]`, err.Error())
|
||||
}
|
||||
return nil, reason.ErrDB.Withf(`Get err[%s]`, err.Error())
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// GetVqdTask Query a single object
|
||||
func (c Core) GetVqdTask(ctx context.Context, id int) (*VqdTask, error) {
|
||||
var out VqdTask
|
||||
@ -86,6 +126,16 @@ func (c Core) GetNameVqdTask(ctx context.Context, name string) (*VqdTask, error)
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
func (c Core) GetNameVqdTaskChannelID(ctx context.Context, name string) (*VqdTask, error) {
|
||||
var out VqdTask
|
||||
if err := c.store.VqdTask().Get(ctx, &out, orm.Where("channel_id=?", name)); err != nil {
|
||||
if orm.IsErrRecordNotFound(err) {
|
||||
return nil, reason.ErrNotFound.Withf(`Get err[%s]`, err.Error())
|
||||
}
|
||||
return nil, reason.ErrDB.Withf(`Get err[%s]`, err.Error())
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// AddVqdTask Insert into database
|
||||
func (c Core) AddVqdTask(ctx context.Context, in *AddVqdTaskInput) (*VqdTask, error) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package vqdtask
|
||||
|
||||
import (
|
||||
"easyvqd/pkg/vqdcms"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
@ -12,7 +13,6 @@ import (
|
||||
// 配置参数
|
||||
const (
|
||||
// 要清理的目标目录(请替换为你实际的目录路径)
|
||||
cleanDir = "/snap"
|
||||
// 定时任务执行间隔(每天执行一次)
|
||||
interval = 24 * time.Hour
|
||||
// 批量删除大小(避免单次删除过多锁表)
|
||||
@ -195,10 +195,10 @@ func (c Core) cleanExpiredFiles() error {
|
||||
expireDays = 1
|
||||
}
|
||||
expireTime := time.Now().Add(-expireDays * 24 * time.Hour)
|
||||
slog.Info(fmt.Sprintf("开始清理目录 [%s] 中超过 %d 天的文件,过期时间阈值:%s", cleanDir, expireDays, expireTime.Format(time.RFC3339)))
|
||||
slog.Info(fmt.Sprintf("开始清理目录 [%s] 中超过 %d 天的文件,过期时间阈值:%s", vqdcms.VQD_IMAGES_DIR, expireDays, expireTime.Format(time.RFC3339)))
|
||||
|
||||
dir, _ := os.Getwd()
|
||||
rootDir := filepath.Join(dir, cleanDir)
|
||||
rootDir := filepath.Join(dir, vqdcms.VQD_IMAGES_DIR)
|
||||
dateDirs, err := os.ReadDir(rootDir)
|
||||
if err != nil {
|
||||
slog.Error("访问根目录路径失败", "path", rootDir, "err", err)
|
||||
|
||||
@ -2,6 +2,7 @@ package vqdtask
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"easyvqd/internal/conf"
|
||||
"easyvqd/internal/core/host"
|
||||
"easyvqd/internal/core/vqd"
|
||||
@ -19,6 +20,7 @@ type Core struct {
|
||||
HostCore *host.Core
|
||||
VqdTaskCore *vqd.Core
|
||||
Cfg *conf.Bootstrap
|
||||
ResultCb vqdcms.VQDResultCB
|
||||
}
|
||||
|
||||
var (
|
||||
@ -31,142 +33,173 @@ func NewCore(HostCore *host.Core, VqdTaskCore *vqd.Core, Cfg *conf.Bootstrap) *C
|
||||
VqdTaskCore: VqdTaskCore,
|
||||
Cfg: Cfg,
|
||||
}
|
||||
core.HostCore.CbIFrame = func(s string, data []byte, codes int) {
|
||||
//fmt.Println("res", s, codes, len(data))
|
||||
v, ok := VqdTaskMap.LoadTaskMap(s)
|
||||
if ok {
|
||||
v.SendData(data, codes)
|
||||
core.ResultCb = func(v vqdcms.AbnormalModel) {
|
||||
|
||||
in := &vqd.AddVqdAlarmInput{
|
||||
ChannelName: v.ChannelName,
|
||||
TaskTemplateName: v.TemplateName,
|
||||
TaskName: v.TaskName,
|
||||
PlanName: v.PlanName,
|
||||
TaskID: int64(v.ID),
|
||||
TaskTemplateID: int64(v.TemplateID),
|
||||
PlanID: int64(v.PlanID),
|
||||
IsDeep: v.IsDeep,
|
||||
FilePath: v.FilePath,
|
||||
}
|
||||
var Abnormals vqd.Abnormals
|
||||
if len(v.Abnormals) > 0 {
|
||||
for _, abnormal := range v.Abnormals {
|
||||
item := vqd.Abnormal{
|
||||
Value: abnormal.Value,
|
||||
Name: abnormal.Name,
|
||||
}
|
||||
Abnormals = append(Abnormals, item)
|
||||
}
|
||||
}
|
||||
in.Abnormals = Abnormals
|
||||
|
||||
var DefaultValues vqd.DefaultValues
|
||||
if len(v.DefaultValues) > 0 {
|
||||
for _, defaultValue := range v.DefaultValues {
|
||||
item := vqd.DefaultValue{
|
||||
Thr1: defaultValue.Thr1,
|
||||
Name1: defaultValue.Name1,
|
||||
Thr2: defaultValue.Thr2,
|
||||
Name2: defaultValue.Name2,
|
||||
Ratio: defaultValue.Ratio,
|
||||
}
|
||||
DefaultValues = append(DefaultValues, item)
|
||||
}
|
||||
}
|
||||
in.DefaultValues = DefaultValues
|
||||
_, err := core.VqdTaskCore.AddVqdAlarm(context.TODO(), in)
|
||||
if err != nil {
|
||||
slog.Error("add alarm", "err", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
core.HostCore.CbIFrame = func(s string, data []byte, codes int) {
|
||||
v, ok := VqdTaskMap.LoadTaskMap(s)
|
||||
if ok {
|
||||
v.SendData(data, VIDEO_CODEC_H265)
|
||||
}
|
||||
//slog.Debug("cb IFrame", "name", s, "codes", codes)
|
||||
}
|
||||
time.AfterFunc(time.Duration(5)*time.Second, func() {
|
||||
//in := &vqd.AddVqdAlarmInput{
|
||||
// AlarmName: "遮挡告警",
|
||||
// AlarmValue: "",
|
||||
// ChannelID: "",
|
||||
// ChannelName: "",
|
||||
// TaskTemplateID: 0,
|
||||
// TaskTemplateName: "",
|
||||
// TaskID: 0,
|
||||
// TaskName: "",
|
||||
// FilePath: "",
|
||||
//}
|
||||
//for i := 0; i < 2; i++ {
|
||||
// core.VqdTaskCore.AddVqdAlarm(context.TODO(), in)
|
||||
//}
|
||||
|
||||
// 启用诊断分析
|
||||
core.InitVqdTask()
|
||||
core.AddTaskVqd(1, "PVWPQBPIv32UI_01")
|
||||
})
|
||||
// 启用定时清理任务
|
||||
go core.scheduleCleanTask()
|
||||
// 测试
|
||||
//go core.OpenStartVqd()
|
||||
|
||||
// 启用任务管理器
|
||||
return core
|
||||
}
|
||||
|
||||
func (c Core) InitVqdTask() {
|
||||
func (c *Core) InitVqdTask() {
|
||||
err := vqdcms.VQDInit()
|
||||
if err != nil {
|
||||
slog.Error("vqd cms open", "err", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
all, _, err := c.VqdTaskCore.FindVqdTaskAll()
|
||||
if err == nil {
|
||||
for _, vqdTask := range all {
|
||||
c.AddTaskVqd(vqdTask.ID)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
func (c Core) UnVqdTask() {
|
||||
func (c *Core) UnVqdTask() {
|
||||
VqdTaskMap.DeleteTaskMapAll()
|
||||
vqdcms.VQDUnInit()
|
||||
return
|
||||
}
|
||||
func (c Core) AddTaskVqd(taskId int, chnId string) {
|
||||
cb := func(res vqdcms.AbnormalModel) {
|
||||
fmt.Println("res", res)
|
||||
}
|
||||
para := vqdcms.NewVQDPara()
|
||||
v := vqdcms.NewVQDHandle(cb, taskId, chnId).Create(para)
|
||||
VqdTaskMap.StoreChildMap(fmt.Sprintf("%s", chnId), v)
|
||||
}
|
||||
func (c *Core) AddTaskVqd(taskId int) error {
|
||||
|
||||
//func (c Core) OpenStartVqd() {
|
||||
//
|
||||
// err := vqdcms.VQDInit()
|
||||
// if err != nil {
|
||||
// fmt.Println("程序异常", err.Error())
|
||||
// return
|
||||
// }
|
||||
// dir, _ := os.Getwd()
|
||||
// rootPath := filepath.Join(dir, "gbs_buf264") // 你的H.264裸流文件路径
|
||||
//
|
||||
// v := vqdcms.NewVQDHandle(nil, 1)
|
||||
//
|
||||
// para := vqdcms.NewVQDPara()
|
||||
// v.SetVQDConfig(para)
|
||||
// v.Create(para)
|
||||
// entries, err := os.ReadDir(rootPath)
|
||||
// if err != nil {
|
||||
// fmt.Printf("读取目录失败: %v\n", err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// fmt.Printf("目录 %s 下的内容:\n", dir)
|
||||
// for _, entry := range entries {
|
||||
// if entry.IsDir() {
|
||||
// fmt.Printf("[目录] %s\n", entry.Name())
|
||||
// } else {
|
||||
// h264Paths := filepath.Join(rootPath, entry.Name()) // 你的H.264裸流文件路径
|
||||
// fmt.Println(h264Paths)
|
||||
// data, err := os.ReadFile(h264Paths)
|
||||
// if err == nil {
|
||||
// datap := GetIFramePointer(data)
|
||||
// width, height, buf, err := v.de.PushDataEx(uintptr(datap.Pointer), datap.Length, VIDEO_CODEC_H264)
|
||||
// if err == nil {
|
||||
// v.SendData(buf, width, height)
|
||||
// slog.Info("I帧转YUV成功: ", "h264Paths", h264Paths)
|
||||
// } else {
|
||||
// //slog.Error("I帧转YUV失败: ", "h264Paths", h264Paths )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return
|
||||
//}
|
||||
//
|
||||
//// H264IFrameData 封装I帧数据和指针信息
|
||||
//type H264IFrameData struct {
|
||||
// Data []byte // I帧原始字节数据
|
||||
// Pointer unsafe.Pointer // 指向数据的原始指针
|
||||
// Length int // 数据长度(字节数)
|
||||
// IsValid bool // 指针是否有效
|
||||
//}
|
||||
//
|
||||
//// GetIFramePointer 将字节切片转换为原始指针
|
||||
//// 注意:unsafe包的使用会绕过Go的内存安全检查,需谨慎
|
||||
//func GetIFramePointer(data []byte) *H264IFrameData {
|
||||
// if len(data) == 0 {
|
||||
// return &H264IFrameData{
|
||||
// IsValid: false,
|
||||
// Length: 0,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 方式1:直接通过unsafe获取切片底层数组的指针(推荐,高效)
|
||||
// // 切片的底层结构是:指向数组的指针 + 长度 + 容量
|
||||
// ptr := unsafe.Pointer(&data[0])
|
||||
//
|
||||
// // 方式2:通过reflect获取指针(更直观展示切片结构,可选)
|
||||
// // sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&data))
|
||||
// // ptr := unsafe.Pointer(sliceHeader.Data)
|
||||
//
|
||||
// return &H264IFrameData{
|
||||
// Data: data,
|
||||
// Pointer: ptr,
|
||||
// Length: len(data),
|
||||
// IsValid: true,
|
||||
// }
|
||||
//}
|
||||
task, err := c.VqdTaskCore.GetVqdTask(context.TODO(), taskId)
|
||||
if err != nil {
|
||||
slog.Error("vqd add task find", "err", err.Error())
|
||||
return err
|
||||
}
|
||||
taskTemplate, err := c.VqdTaskCore.GetIDVqdTaskTemplate(context.TODO(), task.TaskTemplateID)
|
||||
if err != nil {
|
||||
slog.Error("vqd add task find template", "err", err.Error())
|
||||
return err
|
||||
}
|
||||
taskPlan, err := c.VqdTaskCore.GetVqdTimeTemplate(context.TODO(), int(task.TimeTemplateID))
|
||||
if err != nil {
|
||||
slog.Error("vqd add task find plan", "err", err.Error())
|
||||
return err
|
||||
}
|
||||
chnId := task.ChannelID
|
||||
|
||||
para := vqdcms.NewVQDPara(taskTemplate)
|
||||
info := vqdcms.VQDHandleInfo{
|
||||
ChannelID: chnId,
|
||||
ChannelName: task.ChannelName,
|
||||
TaskID: task.ID,
|
||||
TaskName: task.Name,
|
||||
TemplateID: taskTemplate.ID,
|
||||
TemplateName: taskTemplate.Name,
|
||||
PlanID: taskPlan.ID,
|
||||
PlanName: taskPlan.Name,
|
||||
Plans: taskPlan.Plans,
|
||||
}
|
||||
v := vqdcms.NewVQDHandle(c.ResultCb, c.HostCore, info).Create(para, taskPlan.Plans)
|
||||
|
||||
VqdTaskMap.StoreChildMap(fmt.Sprintf("%s", chnId), v)
|
||||
return nil
|
||||
}
|
||||
func (c *Core) UpdateTaskVqd(taskId int) error {
|
||||
task, err := c.VqdTaskCore.GetVqdTask(context.TODO(), taskId)
|
||||
if err != nil {
|
||||
slog.Error("vqd update task find", "err", err.Error())
|
||||
return err
|
||||
}
|
||||
v, ok := VqdTaskMap.LoadTaskMap(task.ChannelID)
|
||||
if ok {
|
||||
taskTemplate, err := c.VqdTaskCore.GetIDVqdTaskTemplate(context.TODO(), task.TaskTemplateID)
|
||||
if err != nil {
|
||||
slog.Error("vqd update task find template", "err", err.Error())
|
||||
return err
|
||||
}
|
||||
taskPlan, err := c.VqdTaskCore.GetVqdTimeTemplate(context.TODO(), int(task.TimeTemplateID))
|
||||
if err != nil {
|
||||
slog.Error("vqd add task find plan", "err", err.Error())
|
||||
return err
|
||||
}
|
||||
para := vqdcms.NewVQDPara(taskTemplate)
|
||||
info := vqdcms.VQDHandleInfo{
|
||||
ChannelID: task.ChannelID,
|
||||
ChannelName: task.ChannelName,
|
||||
TaskID: task.ID,
|
||||
TaskName: task.Name,
|
||||
TemplateID: taskTemplate.ID,
|
||||
TemplateName: taskTemplate.Name,
|
||||
PlanID: taskPlan.ID,
|
||||
PlanName: taskPlan.Name,
|
||||
Plans: taskPlan.Plans,
|
||||
}
|
||||
errs := v.SetVQDConfig(para, info)
|
||||
if errs != nil {
|
||||
slog.Error("vqd update set config err", "err", errs.Error())
|
||||
return err
|
||||
}
|
||||
VqdTaskMap.StoreChildMap(fmt.Sprintf("%s", task.ChannelID), v)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
func (c *Core) DelTaskVqd(taskId int, chnId string) {
|
||||
v, ok := VqdTaskMap.LoadTaskMap(chnId)
|
||||
if ok {
|
||||
v.Destroy()
|
||||
}
|
||||
VqdTaskMap.DeleteTaskMap(chnId)
|
||||
}
|
||||
|
||||
// 测试i帧数据是否可以转为图片文件
|
||||
func CheckIFramesToJpg() {
|
||||
|
||||
@ -4,3 +4,5 @@ const (
|
||||
VIDEO_CODEC_H264 = 0x1C
|
||||
VIDEO_CODEC_H265 = 0xAE
|
||||
)
|
||||
|
||||
//status
|
||||
|
||||
@ -56,8 +56,8 @@ func setupRouter(r *gin.Engine, uc *Usecase) {
|
||||
r.GET("/app/metrics/api", web.WrapH(uc.getMetricsAPI))
|
||||
//快照
|
||||
dir, _ := os.Getwd()
|
||||
uploadsDir := filepath.Join(dir, "uploads")
|
||||
r.Use(statics.Serve("/uploads", statics.LocalFile(uploadsDir, true)))
|
||||
uploadsDir := filepath.Join(dir, "snap")
|
||||
r.Use(statics.Serve("/snap", statics.LocalFile(uploadsDir, true)))
|
||||
|
||||
versionapi.Register(r, uc.Version, auth)
|
||||
registerConfig(r, ConfigAPI{uc: uc, cfg: uc.Conf})
|
||||
@ -77,9 +77,9 @@ func setupRouter(r *gin.Engine, uc *Usecase) {
|
||||
ctx.Redirect(http.StatusTemporaryRedirect, target)
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(p, "/uploads/") {
|
||||
if strings.HasPrefix(p, "/snap/") {
|
||||
q := ctx.Request.URL.RawQuery
|
||||
target := "/uploads/"
|
||||
target := "/snap/"
|
||||
if q != "" {
|
||||
target = target + "?" + q
|
||||
}
|
||||
|
||||
@ -16,15 +16,18 @@ func (a VqdTaskAPI) findVqdAlarm(c *gin.Context, in *vqd.FindVqdAlarmInput) (any
|
||||
//row := structs.Map(item)
|
||||
row := make(map[string]interface{})
|
||||
row["id"] = item.ID
|
||||
row["alarm_name"] = item.AlarmName
|
||||
row["alarm_value"] = item.AlarmValue
|
||||
row["is_deep"] = item.IsDeep
|
||||
row["channel_id"] = item.ChannelID
|
||||
row["channel_name"] = item.ChannelName
|
||||
row["plan_id"] = item.PlanID
|
||||
row["plan_name"] = item.PlanName
|
||||
row["task_template_id"] = item.TaskTemplateID
|
||||
row["task_template_name"] = item.TaskTemplateName
|
||||
row["task_id"] = item.TaskID
|
||||
row["task_name"] = item.TaskName
|
||||
row["file_path"] = item.FilePath
|
||||
row["abnormals"] = item.Abnormals
|
||||
row["default_values"] = item.DefaultValues
|
||||
row["created_at"] = item.CreatedAt
|
||||
row["updated_at"] = item.UpdatedAt
|
||||
|
||||
@ -42,15 +45,18 @@ func (a VqdTaskAPI) getVqdAlarm(c *gin.Context, _ *struct{}) (any, error) {
|
||||
row := make(map[string]interface{})
|
||||
|
||||
row["id"] = item.ID
|
||||
row["alarm_name"] = item.AlarmName
|
||||
row["alarm_value"] = item.AlarmValue
|
||||
row["is_deep"] = item.IsDeep
|
||||
row["channel_id"] = item.ChannelID
|
||||
row["channel_name"] = item.ChannelName
|
||||
row["plan_id"] = item.PlanID
|
||||
row["plan_name"] = item.PlanName
|
||||
row["task_template_id"] = item.TaskTemplateID
|
||||
row["task_template_name"] = item.TaskTemplateName
|
||||
row["task_id"] = item.TaskID
|
||||
row["task_name"] = item.TaskName
|
||||
row["file_path"] = item.FilePath
|
||||
row["abnormals"] = item.Abnormals
|
||||
row["default_values"] = item.DefaultValues
|
||||
row["created_at"] = item.CreatedAt
|
||||
row["updated_at"] = item.UpdatedAt
|
||||
|
||||
|
||||
@ -82,6 +82,13 @@ func (a VqdTaskAPI) findVqdTask(c *gin.Context, in *vqd.FindVqdTaskInput) (any,
|
||||
if errs == nil && template != nil {
|
||||
row["time_template_name"] = timeTemplate.Name
|
||||
}
|
||||
row["error_msg"] = ""
|
||||
row["status"] = 0
|
||||
v, ok := vqdtask.VqdTaskMap.LoadTaskMap(item.ChannelID)
|
||||
if ok {
|
||||
row["error_msg"] = v.ErrorMsg
|
||||
row["status"] = v.Status
|
||||
}
|
||||
row["created_at"] = item.CreatedAt
|
||||
row["updated_at"] = item.UpdatedAt
|
||||
|
||||
@ -115,6 +122,13 @@ func (a VqdTaskAPI) getVqdTask(c *gin.Context, _ *struct{}) (any, error) {
|
||||
if errs == nil && template != nil {
|
||||
row["time_template_name"] = timeTemplate.Name
|
||||
}
|
||||
row["error_msg"] = ""
|
||||
row["status"] = 0
|
||||
v, ok := vqdtask.VqdTaskMap.LoadTaskMap(item.ChannelID)
|
||||
if ok {
|
||||
row["error_msg"] = v.ErrorMsg
|
||||
row["status"] = v.Status
|
||||
}
|
||||
row["created_at"] = item.CreatedAt
|
||||
row["updated_at"] = item.UpdatedAt
|
||||
|
||||
@ -122,34 +136,62 @@ func (a VqdTaskAPI) getVqdTask(c *gin.Context, _ *struct{}) (any, error) {
|
||||
return gin.H{"data": row}, err
|
||||
}
|
||||
func (a VqdTaskAPI) addVqdTask(c *gin.Context, in *vqd.AddVqdTaskInput) (any, error) {
|
||||
_, err := a.core.AddVqdTask(c.Request.Context(), in)
|
||||
taskChn, err := a.core.GetNameVqdTaskChannelID(c.Request.Context(), in.ChannelID)
|
||||
if err == nil && taskChn != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`通道任务已存在 [%s] `, taskChn.ChannelID))
|
||||
}
|
||||
|
||||
task, err := a.core.AddVqdTask(c.Request.Context(), in)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add vqdcms err [%s]`, err.Error()))
|
||||
}
|
||||
|
||||
err = a.vqdSdkCore.AddTaskVqd(task.ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add task err [%s]`, err.Error()))
|
||||
}
|
||||
return gin.H{"data": "OK!"}, err
|
||||
}
|
||||
|
||||
func (a VqdTaskAPI) editVqdTask(c *gin.Context, in *vqd.EditVqdTaskInput) (any, error) {
|
||||
ID, _ := strconv.Atoi(c.Param("id"))
|
||||
_, err := a.core.GetVqdTask(c.Request.Context(), ID)
|
||||
task, err := a.core.GetVqdTask(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
|
||||
}
|
||||
|
||||
_, err = a.core.EditVqdTask(c.Request.Context(), in, ID)
|
||||
if task.ChannelID != in.ChannelID {
|
||||
taskChn, errs := a.core.GetNameVqdTaskChannelID(c.Request.Context(), in.ChannelID)
|
||||
if errs == nil && taskChn != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`通道任务已存在 [%s] `, taskChn.ChannelID))
|
||||
}
|
||||
}
|
||||
info, err := a.core.EditVqdTask(c.Request.Context(), in, ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`edit vqdcms err [%s]`, err.Error()))
|
||||
}
|
||||
if task.Enable == false && in.Enable {
|
||||
err = a.vqdSdkCore.AddTaskVqd(task.ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add task err [%s]`, err.Error()))
|
||||
}
|
||||
} else {
|
||||
if info.Enable {
|
||||
err = a.vqdSdkCore.UpdateTaskVqd(ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`update task err [%s]`, err.Error()))
|
||||
}
|
||||
} else {
|
||||
a.vqdSdkCore.DelTaskVqd(ID, info.ChannelID)
|
||||
}
|
||||
}
|
||||
return gin.H{"data": "OK!"}, err
|
||||
}
|
||||
|
||||
func (a VqdTaskAPI) delVqdTask(c *gin.Context, _ *struct{}) (any, error) {
|
||||
ID, _ := strconv.Atoi(c.Param("id"))
|
||||
_, err := a.core.DelVqdTask(c.Request.Context(), ID)
|
||||
task, err := a.core.DelVqdTask(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqdcms [%d] err [%s]`, ID, err.Error()))
|
||||
}
|
||||
|
||||
a.vqdSdkCore.DelTaskVqd(ID, task.ChannelID)
|
||||
return gin.H{"data": "OK!"}, err
|
||||
}
|
||||
|
||||
@ -69,17 +69,27 @@ func (a VqdTaskAPI) getVqdTaskTemplate(c *gin.Context, _ *struct{}) (any, error)
|
||||
return gin.H{"data": row}, err
|
||||
}
|
||||
func (a VqdTaskAPI) addVqdTaskTemplate(c *gin.Context, in *vqd.AddVqdTaskTemplateInput) (any, error) {
|
||||
_, err := a.core.AddVqdTaskTemplate(c.Request.Context(), in)
|
||||
template, err := a.core.AddVqdTaskTemplate(c.Request.Context(), in)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add vqdcms err [%s]`, err.Error()))
|
||||
}
|
||||
out, _, err := a.core.FindVqdTemplateID(c.Request.Context(), template.ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find template all err [%s]`, err.Error()))
|
||||
}
|
||||
for _, task := range out {
|
||||
err = a.vqdSdkCore.UpdateTaskVqd(task.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return gin.H{"data": "OK!"}, err
|
||||
}
|
||||
|
||||
func (a VqdTaskAPI) editVqdTaskTemplate(c *gin.Context, in *vqd.EditVqdTaskTemplateInput) (any, error) {
|
||||
ID, _ := strconv.Atoi(c.Param("id"))
|
||||
_, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID)
|
||||
template, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
|
||||
}
|
||||
@ -88,6 +98,16 @@ func (a VqdTaskAPI) editVqdTaskTemplate(c *gin.Context, in *vqd.EditVqdTaskTempl
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`edit vqdcms err [%s]`, err.Error()))
|
||||
}
|
||||
out, _, err := a.core.FindVqdTemplateID(c.Request.Context(), template.ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find template all err [%s]`, err.Error()))
|
||||
}
|
||||
for _, task := range out {
|
||||
err = a.vqdSdkCore.UpdateTaskVqd(task.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return gin.H{"data": "OK!"}, err
|
||||
}
|
||||
|
||||
|
||||
@ -49,17 +49,27 @@ func (a VqdTaskAPI) getVqdTimeTemplate(c *gin.Context, _ *struct{}) (any, error)
|
||||
return gin.H{"data": row}, err
|
||||
}
|
||||
func (a VqdTaskAPI) addVqdTimeTemplate(c *gin.Context, in *vqd.AddVqdTimeTemplateInput) (any, error) {
|
||||
_, err := a.core.AddVqdTimeTemplate(c.Request.Context(), in)
|
||||
plan, err := a.core.AddVqdTimeTemplate(c.Request.Context(), in)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add vqdcms err [%s]`, err.Error()))
|
||||
}
|
||||
|
||||
out, _, err := a.core.FindVqdPlanID(c.Request.Context(), plan.ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find plan all err [%s]`, err.Error()))
|
||||
}
|
||||
for _, task := range out {
|
||||
err = a.vqdSdkCore.UpdateTaskVqd(task.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return gin.H{"data": "OK!"}, err
|
||||
}
|
||||
|
||||
func (a VqdTaskAPI) editVqdTimeTemplate(c *gin.Context, in *vqd.EditVqdTimeTemplateInput) (any, error) {
|
||||
ID, _ := strconv.Atoi(c.Param("id"))
|
||||
_, err := a.core.GetVqdTimeTemplate(c.Request.Context(), ID)
|
||||
plan, err := a.core.GetVqdTimeTemplate(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
|
||||
}
|
||||
@ -68,6 +78,16 @@ func (a VqdTaskAPI) editVqdTimeTemplate(c *gin.Context, in *vqd.EditVqdTimeTempl
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`edit vqdcms err [%s]`, err.Error()))
|
||||
}
|
||||
out, _, err := a.core.FindVqdPlanID(c.Request.Context(), plan.ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find plan all err [%s]`, err.Error()))
|
||||
}
|
||||
for _, task := range out {
|
||||
err = a.vqdSdkCore.UpdateTaskVqd(task.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return gin.H{"data": "OK!"}, err
|
||||
}
|
||||
|
||||
@ -78,14 +98,14 @@ func (a VqdTaskAPI) delVqdTimeTemplate(c *gin.Context, _ *struct{}) (any, error)
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
|
||||
}
|
||||
if info.IsDefault {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`默认模板不支持删除 [%s] `, info.Name))
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`计划模板不支持删除 [%s] `, info.Name))
|
||||
}
|
||||
planInfo, err := a.core.FindVqdTaskPlanID(c.Request.Context(), ID)
|
||||
if err == nil {
|
||||
if planInfo != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`计划关联【%s】任务,需先清理【%s】任务后才能删除模板!`, planInfo.Name, planInfo.Name))
|
||||
}
|
||||
}
|
||||
//templateInfo, err := a.core.FindVqdTimeTemplateID(c.Request.Context(), ID)
|
||||
//if err == nil {
|
||||
// if templateInfo != nil {
|
||||
// return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`模板关联【%s】任务,需先清理【%s】任务后才能删除模板!`, templateInfo.Name, templateInfo.Name))
|
||||
// }
|
||||
//}
|
||||
_, err = a.core.DelVqdTimeTemplate(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqdcms [%d] err [%s]`, ID, err.Error()))
|
||||
|
||||
@ -20,7 +20,6 @@ import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
@ -29,14 +28,6 @@ type VideoDecoder struct {
|
||||
handle C.EZ_DECODER_HANDLE
|
||||
sync.RWMutex
|
||||
IsStart bool
|
||||
|
||||
file *os.File
|
||||
}
|
||||
|
||||
func (v *VideoDecoder) WriteFile(data string) {
|
||||
if v.file != nil {
|
||||
v.file.WriteString(data)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VideoDecoder) Create() error {
|
||||
@ -45,16 +36,6 @@ func (v *VideoDecoder) Create() error {
|
||||
if v.IsStart {
|
||||
return nil
|
||||
}
|
||||
//if v.file == nil {
|
||||
// filename := filepath.Join(utils.CWD(), "logs", fmt.Sprintf("%016p_decoder.log", v))
|
||||
// file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm)
|
||||
// if err == nil {
|
||||
// v.file = file
|
||||
// v.file.WriteString("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\r\n")
|
||||
// } else {
|
||||
// llog.Info("创建文件失败:%s", filename)
|
||||
// }
|
||||
//}
|
||||
ret := int(C.ez_decoder_create(&v.handle))
|
||||
v.IsStart = true
|
||||
return IsReturnErrorf(ret, "ez_decoder_create")
|
||||
@ -67,43 +48,29 @@ func (v *VideoDecoder) Destroy() error {
|
||||
return nil
|
||||
}
|
||||
v.IsStart = false
|
||||
if v.file != nil {
|
||||
v.file.WriteString("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\r\n")
|
||||
v.file.Close()
|
||||
v.file = nil
|
||||
}
|
||||
slog.Info("====>>ez_decoder_destroy", "v", v)
|
||||
ret := int(C.ez_decoder_destroy(&v.handle))
|
||||
return IsReturnErrorf(ret, "ez_decoder_destroy")
|
||||
}
|
||||
func (v *VideoDecoder) PushDataEx(buf uintptr, size, codec int) (w, h int, data []byte, err error) {
|
||||
func (v *VideoDecoder) PushDataEx(buf []byte, codec int) (w, h int, data []byte, err error) {
|
||||
v.Lock()
|
||||
defer v.Unlock()
|
||||
if !v.IsStart {
|
||||
return 0, 0, nil, fmt.Errorf("decoder fail")
|
||||
}
|
||||
if v.file != nil {
|
||||
v.file.WriteString(fmt.Sprintf("[start] function:PushData;buf len:%d; codec:%x\r\n", size, codec))
|
||||
}
|
||||
cBytes := C.CBytes(buf)
|
||||
defer C.free(unsafe.Pointer(cBytes)) // 必须手动释放
|
||||
info := &C.EZDecoderInfo{}
|
||||
var out_data *C.uchar
|
||||
//llog.Info("=======================>>>>>>:%v:%d", buf, size)
|
||||
ret := int(C.ez_decoder_push_data(v.handle, (*C.uchar)(unsafe.Pointer(buf)), C.int(size), C.int(codec), &out_data, info))
|
||||
//var out_data uintptr
|
||||
//ret := int(C.ez_decoder_push_data(v.handle, (*C.char)(unsafe.Pointer(buf)), C.int(size), C.int(codec), (**C.char)(unsafe.Pointer(&out_data)), info))
|
||||
if v.file != nil {
|
||||
v.file.WriteString(fmt.Sprintf("[end] function:PushData;buf len:%d; codec:%x; ret:%d\r\n", size, codec, ret))
|
||||
}
|
||||
ret := int(C.ez_decoder_push_data(v.handle, (*C.uchar)(cBytes), C.int(len(buf)), C.int(codec), &out_data, info))
|
||||
//return 0, 0, nil, fmt.Errorf("test") // 252
|
||||
if ret == 0 {
|
||||
data_size := int(info.data_size)
|
||||
_w := int(info.width)
|
||||
_h := int(info.height)
|
||||
//llog.Info("=======================>>>>>>789:%v:%p:%d", out_data, v, data_size)
|
||||
yuv := C.GoBytes(unsafe.Pointer(out_data), info.data_size)
|
||||
_data := make([]byte, data_size)
|
||||
copy(_data, yuv)
|
||||
//llog.Info("=======================>>>>>>22222")
|
||||
return _w, _h, _data, nil
|
||||
|
||||
//_data := make([]byte, data_size)
|
||||
|
||||
@ -9,7 +9,7 @@ type VqdTaskMap struct {
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// 读出相应Key的子Map
|
||||
// 读出相应Key的Map
|
||||
func (m *VqdTaskMap) LoadTaskMap(key string) (value *VQDHandle, ok bool) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
@ -17,16 +17,25 @@ func (m *VqdTaskMap) LoadTaskMap(key string) (value *VQDHandle, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// 删除相应Key的子Map
|
||||
// 删除相应Key的Map
|
||||
func (m *VqdTaskMap) DeleteTaskMap(key string) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
delete(m.M, key)
|
||||
}
|
||||
|
||||
// 增加或修改相应Key的子Map
|
||||
// 增加或修改相应Key的Map
|
||||
func (m *VqdTaskMap) StoreChildMap(key string, value *VQDHandle) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
m.M[key] = value
|
||||
}
|
||||
|
||||
// 删除全部的Map
|
||||
func (m *VqdTaskMap) DeleteTaskMapAll() {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
for _, handle := range m.M {
|
||||
handle.Destroy()
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,13 @@ package vqdcms
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"easyvqd/internal/core/vqd"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
const VQD_IMAGES_DIR = "snap"
|
||||
|
||||
type Abnormal struct {
|
||||
Value float32 `json:"value"`
|
||||
Name string `json:"name"`
|
||||
@ -43,11 +46,13 @@ func (r defaultValue) Value() (driver.Value, error) {
|
||||
|
||||
type AbnormalModel struct {
|
||||
ID int `gorm:"primary_key" json:"id"`
|
||||
TaskName string `json:"task_name"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
AlgoID int `json:"algo_id"`
|
||||
AlgoName string `json:"algo_name"`
|
||||
TaskName string `json:"task_name"`
|
||||
PlanID int `json:"plan_id"`
|
||||
PlanName string `json:"plan_name"`
|
||||
TemplateID int `json:"template_id"`
|
||||
TemplateName string `json:"template_name"`
|
||||
FilePath string `json:"file_path"`
|
||||
IsDeep bool `json:"is_deep"`
|
||||
Abnormals abnormal `gorm:"type:json" json:"abnormals"`
|
||||
@ -267,58 +272,95 @@ type VQDPara struct {
|
||||
FlowerPara VQDFlowerPara /* 花屏检测 */
|
||||
}
|
||||
|
||||
// config cvrdo.VQDAlgoConfig
|
||||
func NewVQDPara() VQDPara {
|
||||
// config
|
||||
func NewVQDPara(tem *vqd.VqdTaskTemplate) VQDPara {
|
||||
var strFunc []string
|
||||
if tem.VqdConfig.Enable {
|
||||
strFunc = append(strFunc, "vqd_config")
|
||||
}
|
||||
if tem.VqdLgtDark.Enable {
|
||||
strFunc = append(strFunc, "vqd_lgt_dark")
|
||||
}
|
||||
if tem.VqdBlue.Enable {
|
||||
strFunc = append(strFunc, "vqd_blue")
|
||||
}
|
||||
if tem.VqdClarity.Enable {
|
||||
strFunc = append(strFunc, "vqd_clarity")
|
||||
}
|
||||
if tem.VqdShark.Enable {
|
||||
strFunc = append(strFunc, "vqd_shark")
|
||||
}
|
||||
if tem.VqdFreeze.Enable {
|
||||
strFunc = append(strFunc, "vqd_freeze")
|
||||
}
|
||||
if tem.VqdColor.Enable {
|
||||
strFunc = append(strFunc, "vqd_color")
|
||||
}
|
||||
if tem.VqdOcclusion.Enable {
|
||||
strFunc = append(strFunc, "vqd_occlusion")
|
||||
}
|
||||
if tem.VqdNoise.Enable {
|
||||
strFunc = append(strFunc, "vqd_noise")
|
||||
}
|
||||
if tem.VqdContrast.Enable {
|
||||
strFunc = append(strFunc, "vqd_contrast")
|
||||
}
|
||||
if tem.VqdMosaic.Enable {
|
||||
strFunc = append(strFunc, "vqd_mosaic")
|
||||
}
|
||||
if tem.VqdFlower.Enable {
|
||||
strFunc = append(strFunc, "vqd_flower")
|
||||
}
|
||||
EnableFunc := GetAlgoEnable(strFunc)
|
||||
return VQDPara{
|
||||
VecFrmNum: 2,
|
||||
UseDeepLearning: true,
|
||||
EnableFunc: GetAlgoEnable([]string{"vqd_config", "vqd_lgt_dark", "vqd_blue", "vqd_clarity", "vqd_shark", "vqd_freeze",
|
||||
"vqd_color", "vqd_occlusion", "vqd_noise", "vqd_contrast", "vqd_mosaic", "vqd_flower"}),
|
||||
VecFrmNum: int(tem.VqdConfig.FrmNum),
|
||||
UseDeepLearning: tem.VqdConfig.IsDeepLearn,
|
||||
EnableFunc: EnableFunc,
|
||||
ColorPara: VQDColorPara{
|
||||
ColorThr: 0.3,
|
||||
ColorAbnNumRatio: 0.6,
|
||||
ColorThr: tem.VqdColor.ColorThr,
|
||||
ColorAbnNumRatio: tem.VqdColor.ColorAbnNumRatio,
|
||||
},
|
||||
LgtDarkPara: VQDLgtDarkPara{
|
||||
LightThr: 0.3,
|
||||
DarkThr: 0.4,
|
||||
LgtDarkAbnNumRatio: 0.6,
|
||||
LightThr: tem.VqdLgtDark.LgtThr,
|
||||
DarkThr: tem.VqdLgtDark.DarkThr,
|
||||
LgtDarkAbnNumRatio: tem.VqdLgtDark.LgtDarkAbnNumRatio,
|
||||
},
|
||||
ClarityPara: VQDClarityPara{
|
||||
ClarityThr: 0.5,
|
||||
ClarityAbnNumRatio: 0.6,
|
||||
ClarityThr: tem.VqdClarity.ClarityThr,
|
||||
ClarityAbnNumRatio: tem.VqdClarity.ClarityAbnNumRatio,
|
||||
},
|
||||
NoisePara: VQDNoisePara{
|
||||
NoiseThr: 0.3,
|
||||
NoiseAbnNumRatio: 0.6,
|
||||
NoiseThr: tem.VqdNoise.NoiseThr,
|
||||
NoiseAbnNumRatio: tem.VqdNoise.NoiseAbnNumRatio,
|
||||
},
|
||||
ContrastPara: VQDContrastPara{
|
||||
CtraLowThr: 0.3,
|
||||
CtraHighThr: 0.8,
|
||||
CtraAbnNumRatio: 0.6,
|
||||
CtraLowThr: tem.VqdContrast.CtraLowThr,
|
||||
CtraHighThr: tem.VqdContrast.CtraHighThr,
|
||||
CtraAbnNumRatio: tem.VqdContrast.CtraAbnNumRatio,
|
||||
},
|
||||
OcclusionPara: VQDOcclusionPara{
|
||||
OcclusionThr: 0.1,
|
||||
OcclusionAbnNumRatio: 0.6,
|
||||
OcclusionThr: tem.VqdOcclusion.OcclusionThr,
|
||||
OcclusionAbnNumRatio: tem.VqdOcclusion.OcclusionAbnNumRatio,
|
||||
},
|
||||
BluePara: VQDBluePara{
|
||||
BlueThr: 0.3,
|
||||
BlueAbnNumRatio: 0.6,
|
||||
BlueThr: tem.VqdBlue.BlueThr,
|
||||
BlueAbnNumRatio: tem.VqdBlue.BlueAbnNumRatio,
|
||||
},
|
||||
SharkPara: VQDSharkPara{
|
||||
SharkThr: 0.2,
|
||||
SharkAbnNumRatio: 0.2,
|
||||
SharkThr: tem.VqdShark.SharkThr,
|
||||
SharkAbnNumRatio: tem.VqdShark.SharkAbnNumRatio,
|
||||
},
|
||||
FreezePara: VQDFreezePara{
|
||||
FreezeThr: 0.999,
|
||||
FreezeAbnNumRatio: 0.99,
|
||||
FreezeThr: tem.VqdFreeze.FreezeThr,
|
||||
FreezeAbnNumRatio: tem.VqdFreeze.FreezeAbnNumRatio,
|
||||
},
|
||||
MosaicPara: VQDMosaicPara{
|
||||
MosaicThr: 0.3,
|
||||
MosaicAbnNumRatio: 0.6,
|
||||
MosaicThr: tem.VqdMosaic.MosaicThr,
|
||||
MosaicAbnNumRatio: tem.VqdMosaic.MosaicAbnNumRatio,
|
||||
},
|
||||
FlowerPara: VQDFlowerPara{
|
||||
FlowerThr: 0.3,
|
||||
FlowerAbnNumRatio: 0.6,
|
||||
FlowerThr: tem.VqdFlower.FlowerThr,
|
||||
FlowerAbnNumRatio: tem.VqdFlower.FlowerAbnNumRatio,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
package vqdcms
|
||||
|
||||
type VqdTaskStatus int
|
||||
|
||||
const (
|
||||
TaskStatusStopped VqdTaskStatus = iota
|
||||
TaskStatusRunning
|
||||
TaskStatusFailed
|
||||
)
|
||||
const NXU_VQD_DISABLE_ALL = 0x00000000 /* 所有功能关闭 */
|
||||
const NXU_VQD_ENABLE_COLOR = 0x00000001 /* 偏色检测 */
|
||||
const NXU_VQD_ENABLE_LGTDARK = 0x00000002 /* 过亮过暗检测 */
|
||||
|
||||
@ -7,9 +7,10 @@ package vqdcms
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"context"
|
||||
"easyvqd/internal/core/host"
|
||||
"easyvqd/pkg/decoder"
|
||||
"fmt"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -29,20 +30,33 @@ type VideoInfoVQD struct {
|
||||
Params VQDPara
|
||||
IsCreateSuccess bool
|
||||
}
|
||||
|
||||
type VQDHandleInfo struct {
|
||||
TaskID int
|
||||
Plans string
|
||||
TaskName string
|
||||
ChannelID string
|
||||
ChannelName string
|
||||
PlanID int
|
||||
PlanName string
|
||||
TemplateID int
|
||||
TemplateName string
|
||||
}
|
||||
type VQDHandle struct {
|
||||
running uint32
|
||||
TaskID int // 标识ID
|
||||
ChnID string // 通道ID
|
||||
Plans string // 任务计划
|
||||
data chan ChanData
|
||||
dataLock sync.RWMutex
|
||||
cb VQDResultCB
|
||||
handle *VideoInfoVQD
|
||||
name string // 算法名称
|
||||
file *os.File
|
||||
decoder *decoder.VideoDecoder
|
||||
fileLock sync.RWMutex
|
||||
running uint32
|
||||
ID int // 标识ID
|
||||
info VQDHandleInfo
|
||||
plansLock sync.RWMutex
|
||||
playTicker *Scheduler
|
||||
ErrorMsg string
|
||||
data chan ChanData
|
||||
dataLock sync.RWMutex
|
||||
cb VQDResultCB
|
||||
handle *VideoInfoVQD
|
||||
name string // 算法名称
|
||||
decoder *decoder.VideoDecoder
|
||||
fileLock sync.RWMutex
|
||||
hostCore *host.Core
|
||||
Status VqdTaskStatus
|
||||
}
|
||||
|
||||
type VQDResultCB func(AbnormalModel)
|
||||
@ -54,53 +68,91 @@ type ChanData struct {
|
||||
now time.Time
|
||||
}
|
||||
|
||||
func IsCurTimeInRecordPlan(recordPlanNew string, now time.Time) bool {
|
||||
// IsCurTimeInRecordPlan 根据一周每天每小时的开关状态判断当前时间是否为开启状态
|
||||
func IsCurTimeInRecordPlan(schedule string, now time.Time) bool {
|
||||
if len(schedule) != 7*24 {
|
||||
slog.Error("schedule length is not 7*24", "schedule", schedule)
|
||||
return false
|
||||
}
|
||||
dayOfWeek := int(now.Weekday()+6) % 7 // 调整为0-6,对应周一至周日
|
||||
hourOfDay := now.Hour()
|
||||
|
||||
// 计算位置索引
|
||||
index := (dayOfWeek * 24) + hourOfDay
|
||||
|
||||
// 检查索引位置的字符是否为'1'
|
||||
if index >= 0 && index < len(schedule) && schedule[index] == '1' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func NewVQDHandle(cb VQDResultCB, taskId int, chnId string) *VQDHandle {
|
||||
func NewVQDHandle(cb VQDResultCB, hostCore *host.Core, info VQDHandleInfo) *VQDHandle {
|
||||
v := &VQDHandle{
|
||||
running: 0,
|
||||
decoder: &decoder.VideoDecoder{},
|
||||
ChnID: chnId,
|
||||
TaskID: taskId,
|
||||
data: make(chan ChanData, MAX_STREAM_CHAN_NUM),
|
||||
cb: cb,
|
||||
handle: &VideoInfoVQD{},
|
||||
running: 0,
|
||||
decoder: &decoder.VideoDecoder{},
|
||||
info: info,
|
||||
ID: info.TaskID,
|
||||
data: make(chan ChanData, MAX_STREAM_CHAN_NUM),
|
||||
cb: cb,
|
||||
handle: &VideoInfoVQD{},
|
||||
playTicker: NewScheduler(),
|
||||
hostCore: hostCore,
|
||||
Status: TaskStatusStopped,
|
||||
}
|
||||
err := v.decoder.Create()
|
||||
if err != nil {
|
||||
slog.Error("decoder Create ", "taskId", chnId, "err", err)
|
||||
slog.Error("decoder Create ", "taskId", info.ChannelID, "err", err)
|
||||
}
|
||||
v.StartPlay()
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *VQDHandle) SetVQDConfig(params VQDPara) error {
|
||||
func (v *VQDHandle) SetVQDConfig(params VQDPara, info VQDHandleInfo) error {
|
||||
v.plansLock.Lock()
|
||||
v.info = info
|
||||
v.plansLock.Unlock()
|
||||
return v.handle.Config(params, params.EnableFunc, 10)
|
||||
}
|
||||
|
||||
func (v *VQDHandle) GetHandle() *VideoInfoVQD {
|
||||
return v.handle
|
||||
}
|
||||
func (v *VQDHandle) Create(params VQDPara) *VQDHandle {
|
||||
func (v *VQDHandle) StartPlay() {
|
||||
v.Play()
|
||||
v.playTicker.Start(25*time.Second, func() {
|
||||
v.Play()
|
||||
})
|
||||
}
|
||||
func (v *VQDHandle) Play() {
|
||||
if IsCurTimeInRecordPlan(v.info.Plans, time.Now()) {
|
||||
//slog.Info("vqd cms play", "taskId", v.TaskID, "chnId", v.ChnID)
|
||||
_, errs := v.hostCore.Play(context.TODO(), &host.PlayInput{
|
||||
ChannelID: v.info.ChannelID,
|
||||
ActiveSecond: 0,
|
||||
})
|
||||
if errs != nil {
|
||||
slog.Debug("vqd cms play", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, "err", errs)
|
||||
v.ErrorMsg = errs.Error()
|
||||
v.Status = TaskStatusFailed
|
||||
} else {
|
||||
v.ErrorMsg = ""
|
||||
v.Status = TaskStatusRunning
|
||||
}
|
||||
} else {
|
||||
v.Status = TaskStatusStopped
|
||||
}
|
||||
}
|
||||
func (v *VQDHandle) Create(params VQDPara, plan string) *VQDHandle {
|
||||
v.plansLock.Lock()
|
||||
v.info.Plans = plan
|
||||
v.plansLock.Unlock()
|
||||
if atomic.LoadUint32(&v.running) == 1 {
|
||||
return v
|
||||
}
|
||||
//if v.file == nil {
|
||||
// filename := filepath.Join(utils.CWD(), "logs", fmt.Sprintf("%016p_vqdhandle.log", v))
|
||||
// file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm)
|
||||
// if err == nil {
|
||||
// v.fileLock.Lock()
|
||||
// v.file = file
|
||||
// v.file.WriteString("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\r\n")
|
||||
// v.fileLock.Unlock()
|
||||
// } else {
|
||||
// llog.Info("创建文件失败:%s", filename)
|
||||
// }
|
||||
//}
|
||||
err := v.handle.Create(params, params.EnableFunc, 10)
|
||||
if err != nil {
|
||||
//llog.Info("vqd create fail:%s", err.Error())
|
||||
slog.Error("vqd create", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, "fail", err)
|
||||
return v
|
||||
}
|
||||
atomic.StoreUint32(&v.running, 1)
|
||||
@ -109,16 +161,17 @@ func (v *VQDHandle) Create(params VQDPara) *VQDHandle {
|
||||
}
|
||||
|
||||
func (v *VQDHandle) Destroy() {
|
||||
if v.file != nil {
|
||||
v.fileLock.Lock()
|
||||
v.file.WriteString("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\r\n")
|
||||
v.file.Close()
|
||||
v.file = nil
|
||||
v.fileLock.Unlock()
|
||||
}
|
||||
if v.data != nil {
|
||||
close(v.data)
|
||||
}
|
||||
if v.decoder != nil {
|
||||
err := v.decoder.Destroy()
|
||||
if err != nil {
|
||||
slog.Error("vqd decoder destroy", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, "err", err)
|
||||
}
|
||||
}
|
||||
v.decoder = nil
|
||||
v.playTicker.Stop()
|
||||
v.data = nil
|
||||
if atomic.LoadUint32(&v.running) == 1 {
|
||||
atomic.StoreUint32(&v.running, 0)
|
||||
@ -133,10 +186,10 @@ func (v *VQDHandle) RunFrame() {
|
||||
print(fmt.Sprintf("%s\n", string(debug.Stack())))
|
||||
}
|
||||
}()
|
||||
cvqdImgsDir := filepath.Join(CWD(), "vqd_images", fmt.Sprintf("%d", v.TaskID))
|
||||
cvqdImgsDir := filepath.Join(CWD(), VQD_IMAGES_DIR, fmt.Sprintf("%d", v.info.TaskID))
|
||||
cvqdImgsDir = filepath.ToSlash(cvqdImgsDir)
|
||||
if err := os.MkdirAll(cvqdImgsDir, os.ModePerm); err != nil {
|
||||
//llog.Info("%s:%s", cvqdImgsDir, err.Error())
|
||||
slog.Error("vqd create img dir", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, "err", err)
|
||||
}
|
||||
|
||||
hyper := 0
|
||||
@ -149,7 +202,7 @@ func (v *VQDHandle) RunFrame() {
|
||||
return
|
||||
}
|
||||
if len(v.data) >= (MAX_STREAM_CHAN_NUM - 6) {
|
||||
//llog.Info("vqd channel num >= %d", MAX_STREAM_CHAN_NUM)
|
||||
slog.Error("vqd channel num", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, ">= ", MAX_STREAM_CHAN_NUM)
|
||||
hyper = MAX_STREAM_CHAN_NUM / 10
|
||||
}
|
||||
if hyper > 0 {
|
||||
@ -157,16 +210,11 @@ func (v *VQDHandle) RunFrame() {
|
||||
break
|
||||
}
|
||||
cdata = data
|
||||
//if !IsCurTimeInRecordPlan(v.analysisTime, cdata.now) {
|
||||
// continue
|
||||
//}
|
||||
if v.file != nil {
|
||||
v.fileLock.Lock()
|
||||
v.file.WriteString(fmt.Sprintf("vqd START data[%d] w[%d] h[%d]\r\n", len(cdata.data), cdata.w, cdata.h))
|
||||
v.fileLock.Unlock()
|
||||
if !IsCurTimeInRecordPlan(v.info.Plans, cdata.now) {
|
||||
continue
|
||||
}
|
||||
now := time.Now().UnixMilli()
|
||||
fpath := filepath.Join(cvqdImgsDir, fmt.Sprintf("%d_%s_%d.jpg", v.TaskID, v.ChnID, now))
|
||||
fpath := filepath.Join(cvqdImgsDir, fmt.Sprintf("%s_%d_%d_%d.jpg", v.info.ChannelID, v.info.TemplateID, v.info.PlanID, now))
|
||||
fpath = filepath.ToSlash(fpath)
|
||||
result := VQDResult{}
|
||||
ret := v.handle.Frame(cdata.data, cdata.w, cdata.h, index, fpath, &result)
|
||||
@ -174,8 +222,15 @@ func (v *VQDHandle) RunFrame() {
|
||||
index = 0
|
||||
if value, b := v.parseVQD(result); b {
|
||||
value.FilePath = strings.TrimPrefix(filepath.ToSlash(fpath), filepath.ToSlash(CWD()))
|
||||
value.ChannelID = v.ChnID
|
||||
value.AlgoName = v.name
|
||||
value.TaskName = v.info.TaskName
|
||||
value.ID = v.info.TaskID
|
||||
value.ChannelID = v.info.ChannelID
|
||||
value.ChannelName = v.info.ChannelName
|
||||
value.PlanID = v.info.PlanID
|
||||
value.PlanName = v.info.PlanName
|
||||
value.TemplateID = v.info.TemplateID
|
||||
value.TemplateName = v.info.TemplateName
|
||||
|
||||
if v.cb != nil {
|
||||
v.cb(value)
|
||||
} else {
|
||||
@ -184,18 +239,6 @@ func (v *VQDHandle) RunFrame() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.file != nil {
|
||||
me, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
v.fileLock.Lock()
|
||||
v.file.WriteString(fmt.Sprintf("vqd END ret[%d] mem is nil\r\n", ret))
|
||||
v.fileLock.Unlock()
|
||||
} else {
|
||||
v.fileLock.Lock()
|
||||
v.file.WriteString(fmt.Sprintf("vqd END ret[%d] mem[%.2f%s]\r\n", ret, me.UsedPercent, "%"))
|
||||
v.fileLock.Unlock()
|
||||
}
|
||||
}
|
||||
//C.free(fp)
|
||||
//C.free(yuvBuf)
|
||||
index++
|
||||
@ -368,14 +411,10 @@ func (v *VQDHandle) parseVQD(result VQDResult) (AbnormalModel, bool) {
|
||||
return abnormals, isabnormal
|
||||
}
|
||||
func (v *VQDHandle) SendData(buf []byte, _codec int) {
|
||||
dataP := GetIFramePointer(buf)
|
||||
if dataP == nil || !dataP.IsValid {
|
||||
slog.Error("I帧转指针失败: ", "TaskID", v.TaskID)
|
||||
return
|
||||
}
|
||||
w, h, data, err := v.decoder.PushDataEx(uintptr(dataP.Pointer), dataP.Length, _codec)
|
||||
|
||||
w, h, data, err := v.decoder.PushDataEx(buf, _codec)
|
||||
if err != nil {
|
||||
slog.Error("I帧转YUV失败: ", "TaskID", v.TaskID, "err", err)
|
||||
slog.Error("I帧转YUV失败: ", "TaskID", v.info.TaskID, "err", err)
|
||||
return
|
||||
}
|
||||
if len(data) > 0 && v.data != nil {
|
||||
@ -388,9 +427,6 @@ func (v *VQDHandle) SendData(buf []byte, _codec int) {
|
||||
h: h,
|
||||
now: now,
|
||||
}
|
||||
//v.dataLock.Lock()
|
||||
//v.data = append(v.data, d)
|
||||
//v.dataLock.Unlock()
|
||||
v.data <- d
|
||||
}
|
||||
}
|
||||
|
||||
56
pkg/vqdcms/worker.go
Normal file
56
pkg/vqdcms/worker.go
Normal file
@ -0,0 +1,56 @@
|
||||
package vqdcms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Worker struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewWorker() *Worker {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &Worker{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Worker) Stop() {
|
||||
w.cancel() // 显式调用cancel停止worker
|
||||
}
|
||||
|
||||
type Scheduler struct {
|
||||
stopChan chan struct{}
|
||||
}
|
||||
|
||||
func NewScheduler() *Scheduler {
|
||||
return &Scheduler{
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scheduler) Start(interval time.Duration, task func()) {
|
||||
go func() {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
task()
|
||||
case <-s.stopChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Scheduler) Stop() {
|
||||
if s.stopChan != nil {
|
||||
close(s.stopChan)
|
||||
s.stopChan = nil // 防止重复关闭
|
||||
}
|
||||
}
|
||||
@ -36,6 +36,7 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
|
||||
channel_id: task.channel_id,
|
||||
channel_name: task.channel_name,
|
||||
task_template_id: task.task_template_id,
|
||||
time_template_id: task.time_template_id,
|
||||
task_template_name: task.task_template_name,
|
||||
enable: task.enable,
|
||||
};
|
||||
|
||||
@ -114,7 +114,7 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
|
||||
const { mutate: createMutate, isPending: creating } = useMutation({
|
||||
mutationFn: CreateVqdTaskTemplate,
|
||||
onSuccess: () => {
|
||||
message.success("创建任务成功");
|
||||
message.success("创建成功");
|
||||
handleClose();
|
||||
onSuccess?.();
|
||||
},
|
||||
@ -124,7 +124,7 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
|
||||
const { mutate: updateMutate, isPending: updating } = useMutation({
|
||||
mutationFn: UpdateVqdTaskTemplate,
|
||||
onSuccess: () => {
|
||||
message.success("更新任务成功");
|
||||
message.success("更新成功");
|
||||
handleClose();
|
||||
onSuccess?.();
|
||||
},
|
||||
|
||||
@ -167,7 +167,7 @@ const AddVqdTimeTemplate = forwardRef<AddVqdTimeTemplateRef, AddVqdTimeTemplateP
|
||||
const { mutate: createMutate, isPending: creating } = useMutation({
|
||||
mutationFn: CreateVqdTimeTemplate,
|
||||
onSuccess: () => {
|
||||
message.success("创建任务成功");
|
||||
message.success("创建成功");
|
||||
handleClose();
|
||||
onSuccess?.();
|
||||
},
|
||||
@ -177,7 +177,7 @@ const AddVqdTimeTemplate = forwardRef<AddVqdTimeTemplateRef, AddVqdTimeTemplateP
|
||||
const { mutate: updateMutate, isPending: updating } = useMutation({
|
||||
mutationFn: UpdateVqdTimeTemplate,
|
||||
onSuccess: () => {
|
||||
message.success("更新任务成功");
|
||||
message.success("更新成功");
|
||||
handleClose();
|
||||
onSuccess?.();
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useRef, useState, useMemo } from "react";
|
||||
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip, Select, Row, Col, Empty, Pagination, Tag } from "antd";
|
||||
import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip, Select, Row, Col, Empty, Pagination, Tag, Popover } from "antd";
|
||||
import { EditOutlined, DeleteOutlined, InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { GetVqdAlarm, DeleteVqdAlarm, DeleteVqdAlarmAll } from "../api/vqdalarm";
|
||||
import type { VqdAlarmItem } from "../types/vqdalarm";
|
||||
@ -233,7 +233,7 @@ export default function VqdAlarmPage() {
|
||||
loading={moonLoading}
|
||||
/> */}
|
||||
<div className="w-[150px] ml-[20px]">
|
||||
<Select
|
||||
{/* <Select
|
||||
defaultValue={0}
|
||||
className="w-[100%] mr-2"
|
||||
placeholder="选择类型"
|
||||
@ -255,7 +255,7 @@ export default function VqdAlarmPage() {
|
||||
</span>
|
||||
</Space>
|
||||
)}
|
||||
/>
|
||||
/> */}
|
||||
<Filter
|
||||
|
||||
searchLoading={isLoading}
|
||||
@ -274,24 +274,43 @@ export default function VqdAlarmPage() {
|
||||
<AlarmSnap filePath={item.file_path} />
|
||||
<div className="pl-3 pr-2 pb-1">
|
||||
|
||||
<Flex justify="space-between" align="center">
|
||||
<Tag bordered={false} color="#87d068" className="m-0 mt-2">{item.alarm_name}</Tag>
|
||||
<Space className="pr-2"> 通道:{item.channel_name||"2222"}</Space>
|
||||
<Flex align="center" wrap gap="small">
|
||||
{
|
||||
item.abnormals.map((item) => {
|
||||
return (<>
|
||||
<Tag bordered={false} color="#87d068" className="m-0 mt-2">{item.name}</Tag>
|
||||
</>)
|
||||
})
|
||||
}
|
||||
</Flex>
|
||||
<div>
|
||||
<Space className="pt-2"> 任务名称:{item.task_name}</Space>
|
||||
</div>
|
||||
{/* <Space className="pt-2"> 通道:{item.channel_name || item.channel_id}</Space> */}
|
||||
<Flex justify="space-between" align="center">
|
||||
<p className="m-0"> {item.created_at}</p>
|
||||
<Tooltip title="删除">
|
||||
<Popconfirm
|
||||
title="删除告警"
|
||||
description="确定要删除此告警吗?"
|
||||
onConfirm={() => {
|
||||
deleteMutation(item.id)
|
||||
}}
|
||||
>
|
||||
{/* loading={isDelLoading} */}
|
||||
<Button type="text" danger icon={<DeleteOutlined />}></Button>
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
<div>
|
||||
<Popover content={(<>
|
||||
<div>通道: {item.channel_name || item.channel_id}</div>
|
||||
<div>诊断计划: {item.plan_name}</div>
|
||||
<div className="pb-2">诊断模板: {item.task_template_name} </div>
|
||||
</>)} title="详情">
|
||||
<Button type="text" icon={<InfoCircleOutlined />}></Button>
|
||||
</Popover>
|
||||
<Tooltip title="删除">
|
||||
<Popconfirm
|
||||
title="删除告警"
|
||||
description="确定要删除此告警吗?"
|
||||
onConfirm={() => {
|
||||
deleteMutation(item.id)
|
||||
}}
|
||||
>
|
||||
{/* loading={isDelLoading} */}
|
||||
<Button type="text" danger icon={<DeleteOutlined />}></Button>
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
|
||||
</div>
|
||||
</Flex>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useRef, useState, useMemo } from "react";
|
||||
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip, Switch } from "antd";
|
||||
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip, Switch,Popover,Tag } from "antd";
|
||||
import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { GetVqdTask, DeleteVqdTask, UpdateVqdTask } from "../api/vqdtask";
|
||||
@ -160,6 +160,26 @@ export default function VqdTaskPage() {
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "拉流状态",
|
||||
dataIndex: "status",
|
||||
align: "center",
|
||||
render: (text, record) => (
|
||||
<Space>
|
||||
{text == 0 && <Tag color="#ccc">未诊断</Tag>}
|
||||
{text == 1 && <Tag color="#87d068">诊断中</Tag>}
|
||||
{text == 2 &&
|
||||
<Popover content={
|
||||
<>
|
||||
{record.error_msg}
|
||||
</>
|
||||
} title="异常详情">
|
||||
<Tag color="#f50">诊断异常</Tag>
|
||||
</Popover>
|
||||
}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "创建日期",
|
||||
dataIndex: "created_at",
|
||||
|
||||
@ -356,7 +356,9 @@
|
||||
.overflow-y-auto {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
.truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -474,6 +476,10 @@
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.pt-2 {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pl-2 {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
@ -16,22 +16,34 @@ export type VqdAlarmRes = {
|
||||
*/
|
||||
total: number;
|
||||
};
|
||||
|
||||
|
||||
export type Abnormals = {
|
||||
value: number;
|
||||
name: string;
|
||||
}
|
||||
export type DefaultValues = {
|
||||
thr1: number;
|
||||
name1: string;
|
||||
thr2: number;
|
||||
name2: string;
|
||||
ratio: number;
|
||||
}
|
||||
/**
|
||||
* 项
|
||||
*/
|
||||
export type VqdAlarmItem = {
|
||||
id: number;
|
||||
alarm_name: string;
|
||||
alarm_value: string;
|
||||
is_deep: boolean;
|
||||
channel_id: string;
|
||||
channel_name: string;
|
||||
plan_id: number;
|
||||
plan_name: string;
|
||||
task_template_id: number;
|
||||
task_template_name: string;
|
||||
task_id: number;
|
||||
task_name: string;
|
||||
file_path: string;
|
||||
abnormals: Abnormals[];
|
||||
default_values: DefaultValues[];
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
|
||||
|
||||
@ -26,6 +26,8 @@ export type VqdTaskItem = {
|
||||
name: string;
|
||||
channel_id: string;
|
||||
channel_name: string;
|
||||
error_msg: string;
|
||||
status: number;
|
||||
task_template_id: number;
|
||||
time_template_id: number;
|
||||
task_template_name: string;
|
||||
@ -103,6 +105,8 @@ export type VqdTaskDetailRes = {
|
||||
name: string;
|
||||
channel_id: string;
|
||||
channel_name: string;
|
||||
error_msg: string;
|
||||
status: number;
|
||||
task_template_id: number;
|
||||
time_template_id: number;
|
||||
task_template_name: string;
|
||||
|
||||
@ -21,7 +21,7 @@ export default defineConfig(({ mode }) => {
|
||||
// 可选:重写路径
|
||||
// rewrite: (path) => path.replace(/^\/api/, '')
|
||||
},
|
||||
'/uploads': {
|
||||
'/snap': {
|
||||
target: 'http://127.0.0.1:8089',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user