添加诊断

This commit is contained in:
Sake 2026-01-23 18:05:36 +08:00
parent 9cfb302777
commit 83a50fa245
72 changed files with 4700 additions and 505 deletions

BIN
VqdSDK/libdrm.so.2 Normal file

Binary file not shown.

BIN
VqdSDK/libgomp.so.1 Normal file

Binary file not shown.

BIN
VqdSDK/libva-drm.so.1 Normal file

Binary file not shown.

BIN
VqdSDK/libva.so.1 Normal file

Binary file not shown.

BIN
check_tip.h264 Normal file

Binary file not shown.

View File

@ -11,7 +11,7 @@ import (
"easyvqd/internal/conf" "easyvqd/internal/conf"
"easyvqd/internal/core/host" "easyvqd/internal/core/host"
"easyvqd/internal/core/media" "easyvqd/internal/core/media"
"easyvqd/internal/core/vqdsdk" "easyvqd/internal/core/vqdtask"
"easyvqd/internal/data" "easyvqd/internal/data"
"easyvqd/internal/web/api" "easyvqd/internal/web/api"
"log/slog" "log/slog"
@ -30,7 +30,7 @@ func WireApp(bc *conf.Bootstrap, log *slog.Logger) (http.Handler, func(), error)
vqdTaskCore := api.NewVqdTaskCore(db) vqdTaskCore := api.NewVqdTaskCore(db)
hostCore := host.NewCore(bc) hostCore := host.NewCore(bc)
mediaCore := media.NewCore(hostCore) mediaCore := media.NewCore(hostCore)
vqdSdkCore := vqdsdk.NewCore(hostCore, vqdTaskCore, bc) vqdSdkCore := vqdtask.NewCore(hostCore, vqdTaskCore, bc)
vqdTaskAPI := api.NewVqdTaskAPI(vqdTaskCore, mediaCore,vqdSdkCore,hostCore, bc) vqdTaskAPI := api.NewVqdTaskAPI(vqdTaskCore, mediaCore,vqdSdkCore,hostCore, bc)
usecase := &api.Usecase{ usecase := &api.Usecase{
@ -45,6 +45,6 @@ func WireApp(bc *conf.Bootstrap, log *slog.Logger) (http.Handler, func(), error)
} }
handler := api.NewHTTPHandler(usecase) handler := api.NewHTTPHandler(usecase)
return handler, func() { return handler, func() {
//mediaCore.Close() vqdSdkCore.UnVqdTask()
}, nil }, nil
} }

View File

@ -1,7 +1,6 @@
package host package host
import ( import (
"context"
"easyvqd/internal/conf" "easyvqd/internal/conf"
"easyvqd/pkg/pluginheart" "easyvqd/pkg/pluginheart"
"encoding/json" "encoding/json"
@ -12,10 +11,12 @@ import (
"git.lnton.com/lnton/pluginsdk/pkg/plugin" "git.lnton.com/lnton/pluginsdk/pkg/plugin"
) )
type IFrameResultCB func(string, []byte, int)
type Core struct { type Core struct {
Plugin *plugin.PluginSDK // 用于业务GRPC通信 Plugin *plugin.PluginSDK // 用于业务GRPC通信
HttpPlugin *pluginheart.Engine // 用于插件管理HTTP通信 HttpPlugin *pluginheart.Engine // 用于插件管理HTTP通信
RtspConfig *ConfigBaseOutput RtspConfig *ConfigBaseOutput
CbIFrame IFrameResultCB
} }
var StartOk chan struct{} var StartOk chan struct{}
@ -45,29 +46,33 @@ func NewCore(cfg *conf.Bootstrap) *Core {
}, false) }, false)
}() }()
c := &Core{ core := &Core{
Plugin: sdk, Plugin: sdk,
HttpPlugin: engine, HttpPlugin: engine,
} }
sdk.AddResponseHandler("stop", core.stop)
sdk.AddResponseHandler("stop", c.stop) sdk.AddResponseHandler("ping", core.ping)
sdk.AddResponseHandler("ping", c.ping)
// 这部分都是收到响应后的回调 // 这部分都是收到响应后的回调
sdk.AddResponseHandler("findDevices", c.findDevicesRespH) sdk.AddResponseHandler("findDevices", core.findDevicesRespH)
sdk.AddResponseHandler("findChannels", c.findChannelsRespH) sdk.AddResponseHandler("findChannels", core.findChannelsRespH)
sdk.AddResponseHandler("getBaseConfig", c.getBaseConfigRespH) sdk.AddResponseHandler("getBaseConfig", core.getBaseConfigRespH)
sdk.AddResponseHandler("findTalkUrl", c.findTalkUrlRespH) sdk.AddResponseHandler("findTalkUrl", core.findTalkUrlRespH)
sdk.AddResponseHandler("iframeData", core.iframeDataRespH)
config, err := c.GetBaseConfig(context.TODO(), &ConfigBaseInput{Mode: "rtsp"}) // 响应主机下发的 "iframeData" 命令
if err != nil { sdk.AddRequestHandler("iframeData", core.iframeDataReqH)
panic(err)
}
// defer sdk.Close() // defer sdk.Close()
return &Core{ return core
Plugin: sdk, }
RtspConfig: config,
func (c *Core) iframeDataReqH(requestID string, args json.RawMessage) (interface{}, error) {
//slog.Debug("Received 'iframeData' from host", "request_id", requestID, "args", args)
out := IframeOutput{}
if err := json.Unmarshal(args, &out); err != nil {
return nil, nil
} }
c.CbIFrame(out.StreamName, out.Data, out.Codes)
return nil, nil
} }
func (c Core) stop(requestID string, args json.RawMessage) (interface{}, error) { func (c Core) stop(requestID string, args json.RawMessage) (interface{}, error) {
@ -76,6 +81,7 @@ func (c Core) stop(requestID string, args json.RawMessage) (interface{}, error)
} }
func (c Core) ping(requestID string, args json.RawMessage) (interface{}, error) { func (c Core) ping(requestID string, args json.RawMessage) (interface{}, error) {
slog.Debug("Received 'ping' from host", "request_id", requestID, "args", args)
return nil, nil return nil, nil
} }
@ -96,3 +102,7 @@ func (c Core) findTalkUrlRespH(requestID string, args json.RawMessage) (interfac
slog.Debug("Received 'findTalkUrl' from host", "request_id", requestID, "args", args) slog.Debug("Received 'findTalkUrl' from host", "request_id", requestID, "args", args)
return nil, nil 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
}

View File

@ -0,0 +1,9 @@
package host
type IframeOutput struct {
ServerId string `json:"server_id"`
ServerAddr string `json:"server_addr"`
StreamName string `json:"stream_name,omitempty"`
Data []byte `json:"data"`
Codes int `json:"codes"`
}

View File

@ -6,6 +6,7 @@ type Storer interface {
VqdTask() VqdTaskStorer VqdTask() VqdTaskStorer
VqdAlarm() VqdAlarmStorer VqdAlarm() VqdAlarmStorer
VqdTaskTemplate() VqdTaskTemplateStorer VqdTaskTemplate() VqdTaskTemplateStorer
VqdTimeTemplate() VqdTimeTemplateStorer
} }
// Core // Core

View File

@ -18,13 +18,13 @@ const (
type VqdTask struct { type VqdTask struct {
orm.Model orm.Model
Name string `gorm:"column:name;notNull;default:'';comment:名称" json:"name"` // 名称 Name string `gorm:"column:name;notNull;default:'';comment:名称" json:"name"` // 名称
ChannelID string `gorm:"column:channel_id;notNull;default:'';comment:关联通道" json:"channel_id"` // 关联通道 ChannelID string `gorm:"column:channel_id;notNull;default:'';comment:关联通道" json:"channel_id"` // 关联通道
ChannelName string `gorm:"column:channel_name;notNull;default:'';comment:关联通道名称" json:"channel_name"` // 关联通道名称 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"` //关联模板 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"` //关联模板名称 TimeTemplateID int64 `gorm:"column:time_template_id;notNull;default:0;comment:关联计划" json:"time_template_id"` // 关联计划
Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用 Enable bool `gorm:"column:enable;notNull;default:FALSE;comment:启用" json:"enable"` // 启用
Des string `gorm:"column:des;notNull;default:'';comment:描述" json:"des"` // 描述 Des string `gorm:"column:des;notNull;default:'';comment:描述" json:"des"` // 描述
} }
// TableName database table name // TableName database table name
@ -217,8 +217,6 @@ func (i *VqdFlower) Scan(input interface{}) error {
type VqdTaskTemplate struct { type VqdTaskTemplate struct {
orm.Model orm.Model
Name string `gorm:"column:name;notNull;default:'';comment:名称" json:"name"` Name string `gorm:"column:name;notNull;default:'';comment:名称" json:"name"`
Plans string `gorm:"column:plans;notNull;default:'';comment:计划" json:"plans"`
Enable bool `gorm:"column:enable;notNull;default:TRUE;comment:是否启用" json:"enable"`
IsDefault bool `gorm:"column:is_default;notNull;default:FALSE;comment:是否默认" json:"is_default"` IsDefault bool `gorm:"column:is_default;notNull;default:FALSE;comment:是否默认" json:"is_default"`
VqdConfig VqdConfig `gorm:"column:vqd_config;type:jsonb;notNull;default:'{}';comment:诊断基础配置" json:"vqd_config"` // 诊断基础配置 VqdConfig VqdConfig `gorm:"column:vqd_config;type:jsonb;notNull;default:'{}';comment:诊断基础配置" json:"vqd_config"` // 诊断基础配置
VqdLgtDark VqdLgtDark `gorm:"column:vqd_lgt_dark;type:jsonb;notNull;default:'{}';comment:亮度检测" json:"vqd_lgt_dark"` // 亮度检测 VqdLgtDark VqdLgtDark `gorm:"column:vqd_lgt_dark;type:jsonb;notNull;default:'{}';comment:亮度检测" json:"vqd_lgt_dark"` // 亮度检测
@ -240,6 +238,20 @@ func (*VqdTaskTemplate) TableName() string {
return "vqd_task_template" return "vqd_task_template"
} }
type VqdTimeTemplate struct {
orm.Model
Name string `gorm:"column:name;notNull;default:'';comment:名称" json:"name"`
Plans string `gorm:"column:plans;notNull;default:'';comment:计划" json:"plans"`
Enable bool `gorm:"column:enable;notNull;default:TRUE;comment:是否启用" json:"enable"`
IsDefault bool `gorm:"column:is_default;notNull;default:FALSE;comment:是否默认" json:"is_default"`
Des string `gorm:"column:des;notNull;default:'';comment:描述" json:"des"` // 描述
}
// TableName database table name
func (*VqdTimeTemplate) TableName() string {
return "vqd_time_template"
}
type VqdAlarm struct { type VqdAlarm struct {
orm.Model orm.Model
AlarmName string `gorm:"column:alarm_name;notNull;default:'';comment:告警名称" json:"alarm_name"` // 告警名称 AlarmName string `gorm:"column:alarm_name;notNull;default:'';comment:告警名称" json:"alarm_name"` // 告警名称

View File

@ -24,6 +24,9 @@ func (d DB) VqdTask() vqd.VqdTaskStorer {
func (d DB) VqdTaskTemplate() vqd.VqdTaskTemplateStorer { func (d DB) VqdTaskTemplate() vqd.VqdTaskTemplateStorer {
return VqdTaskTemplate(d) return VqdTaskTemplate(d)
} }
func (d DB) VqdTimeTemplate() vqd.VqdTimeTemplateStorer {
return VqdTimeTemplate(d)
}
func (d DB) VqdAlarm() vqd.VqdAlarmStorer { func (d DB) VqdAlarm() vqd.VqdAlarmStorer {
return VqdAlarm(d) return VqdAlarm(d)
} }
@ -36,6 +39,7 @@ func (d DB) AutoMigrate(ok bool) DB {
if err := d.db.AutoMigrate( if err := d.db.AutoMigrate(
new(vqd.VqdTask), new(vqd.VqdTask),
new(vqd.VqdTaskTemplate), new(vqd.VqdTaskTemplate),
new(vqd.VqdTimeTemplate),
new(vqd.VqdAlarm), new(vqd.VqdAlarm),
); err != nil { ); err != nil {
panic(err) panic(err)

View File

@ -0,0 +1,60 @@
// Code generated by gowebx, DO AVOID EDIT.
package audioencodedb
import (
"context"
"easyvqd/internal/core/vqd"
"git.lnton.com/lnton/pkg/orm"
)
var _ vqd.VqdTimeTemplateStorer = VqdTimeTemplate{}
// VqdTimeTemplate Related business namespaces
type VqdTimeTemplate DB
// FindAll implements vqd.VqdTimeTemplateStorer.
func (d VqdTimeTemplate) FindAll(bs *[]*vqd.VqdTimeTemplate) (int64, error) {
db := d.db.Model(&vqd.VqdTimeTemplate{})
var total int64
if err := db.Count(&total).Error; err != nil || total <= 0 {
// 如果统计失败或者数量为0则返回错误
return 0, err
}
return total, db.Find(bs).Error
}
// Find implements vqd.VqdTimeTemplateStorer.
func (d VqdTimeTemplate) Find(ctx context.Context, bs *[]*vqd.VqdTimeTemplate, page orm.Pager, opts ...orm.QueryOption) (int64, error) {
return orm.FindWithContext(ctx, d.db, bs, page, opts...)
}
// Get implements vqd.VqdTimeTemplateStorer.
func (d VqdTimeTemplate) Get(ctx context.Context, model *vqd.VqdTimeTemplate, opts ...orm.QueryOption) error {
return orm.FirstWithContext(ctx, d.db, model, opts...)
}
// Add implements vqd.VqdTimeTemplateStorer.
func (d VqdTimeTemplate) Add(ctx context.Context, model *vqd.VqdTimeTemplate) error {
return d.db.WithContext(ctx).Create(model).Error
}
// Edit implements vqd.VqdTimeTemplateStorer.
func (d VqdTimeTemplate) Edit(ctx context.Context, model *vqd.VqdTimeTemplate, changeFn func(*vqd.VqdTimeTemplate), opts ...orm.QueryOption) error {
return orm.UpdateWithContext(ctx, d.db, model, changeFn, opts...)
}
// Del implements vqd.VqdTimeTemplateStorer.
func (d VqdTimeTemplate) Del(ctx context.Context, model *vqd.VqdTimeTemplate, opts ...orm.QueryOption) error {
return orm.DeleteWithContext(ctx, d.db, model, opts...)
}
// EditStatus implements vqd.VqdTimeTemplateStorer.
func (d VqdTimeTemplate) EditStatus(status vqd.EncodeStatus, id int) error {
if err := d.db.Model(&vqd.VqdTimeTemplate{}).Where(`id = ?`, id).Update("encode_status", status).Error; err != nil {
return err
}
return nil
}
func (d VqdTimeTemplate) FirstOrCreate(b any) error {
return d.db.FirstOrCreate(b).Error
}

View File

@ -11,21 +11,21 @@ type FindVqdTaskInput struct {
} }
type EditVqdTaskInput struct { type EditVqdTaskInput struct {
Name string `json:"name"` // 名称 Name string `json:"name"` // 名称
ChannelID string `json:"channel_id"` // 关联通道 ChannelID string `json:"channel_id"` // 关联通道
ChannelName string `json:"channel_name"` // 通道名称 ChannelName string `json:"channel_name"` // 通道名称
TaskTemplateID int `json:"task_template_id"` // 关联模板 TaskTemplateID int `json:"task_template_id"` // 关联模板
TaskTemplateName string `json:"task_template_name"` // 模板名称 TimeTemplateID int `json:"time_template_id"` // 关联计划
Enable bool `form:"enable"` // 启用 Enable bool `form:"enable"` // 启用
Des string `json:"des"` // 描述 Des string `json:"des"` // 描述
} }
type AddVqdTaskInput struct { type AddVqdTaskInput struct {
Name string `json:"name"` // 名称 Name string `json:"name"` // 名称
ChannelID string `json:"channel_id"` // 关联通道 ChannelID string `json:"channel_id"` // 关联通道
ChannelName string `json:"channel_name"` // 通道名称 ChannelName string `json:"channel_name"` // 通道名称
TaskTemplateID int `json:"task_template_id"` // 关联模板 TaskTemplateID int `json:"task_template_id"` // 关联模板
TaskTemplateName string `json:"task_template_name"` // 模板名称 TimeTemplateID int `json:"time_template_id"` // 关联计划
Enable bool `form:"enable"` // 启用 Enable bool `form:"enable"` // 启用
Des string `json:"des"` // 描述 Des string `json:"des"` // 描述
} }

View File

@ -83,7 +83,7 @@ func (c Core) GetNameVqdTaskTemplate(ctx context.Context, name string) (*VqdTask
} }
// FirstOrCreateTemplate Insert into database // FirstOrCreateTemplate Insert into database
func (c Core) FirstOrCreateTemplate(b any) error { func (c Core) FirstOrCreateTaskTemplate(b any) error {
return c.store.VqdTaskTemplate().FirstOrCreate(b) return c.store.VqdTaskTemplate().FirstOrCreate(b)
} }

View File

@ -0,0 +1,122 @@
// Code generated by gowebx, DO AVOID EDIT.
package vqd
import (
"context"
"git.lnton.com/lnton/pkg/orm"
"git.lnton.com/lnton/pkg/reason"
"github.com/jinzhu/copier"
"log/slog"
)
// VqdTimeTemplateStorer Instantiation interface
type VqdTimeTemplateStorer interface {
Find(context.Context, *[]*VqdTimeTemplate, orm.Pager, ...orm.QueryOption) (int64, error)
FindAll(dp *[]*VqdTimeTemplate) (int64, error)
Get(context.Context, *VqdTimeTemplate, ...orm.QueryOption) error
Add(context.Context, *VqdTimeTemplate) error
Edit(context.Context, *VqdTimeTemplate, func(*VqdTimeTemplate), ...orm.QueryOption) error
Del(context.Context, *VqdTimeTemplate, ...orm.QueryOption) error
FirstOrCreate(b any) error
}
// FindVqdTimeTemplateAll Paginated search
func (c Core) FindVqdTimeTemplateAll() ([]*VqdTimeTemplate, int64, error) {
items := make([]*VqdTimeTemplate, 0)
total, err := c.store.VqdTimeTemplate().FindAll(&items)
if err != nil {
return nil, 0, reason.ErrDB.Withf(`Find err[%s]`, err.Error())
}
return items, total, nil
}
// FindVqdTimeTemplate Paginated search
func (c Core) FindVqdTimeTemplate(ctx context.Context, in *FindVqdTimeTemplateInput) ([]*VqdTimeTemplate, int64, error) {
items := make([]*VqdTimeTemplate, 0)
if in.Name != "" {
query := orm.NewQuery(8).Where("name like ? ", "%"+in.Name+"%")
total, err := c.store.VqdTimeTemplate().Find(ctx, &items, in, query.Encode()...)
if err != nil {
return nil, 0, reason.ErrDB.Withf(`Find err[%s]`, err.Error())
}
return items, total, nil
} else {
total, err := c.store.VqdTimeTemplate().Find(ctx, &items, in)
if err != nil {
return nil, 0, reason.ErrDB.Withf(`Find err[%s]`, err.Error())
}
return items, total, nil
}
}
// GetVqdTimeTemplate Query a single object
func (c Core) GetVqdTimeTemplate(ctx context.Context, id int) (*VqdTimeTemplate, error) {
var out VqdTimeTemplate
if err := c.store.VqdTimeTemplate().Get(ctx, &out, orm.Where("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
}
func (c Core) GetIDVqdTimeTemplate(ctx context.Context, ID int64) (*VqdTimeTemplate, error) {
var out VqdTimeTemplate
if err := c.store.VqdTimeTemplate().Get(ctx, &out, orm.Where("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
}
func (c Core) GetNameVqdTimeTemplate(ctx context.Context, name string) (*VqdTimeTemplate, error) {
var out VqdTimeTemplate
if err := c.store.VqdTimeTemplate().Get(ctx, &out, orm.Where("name=?", 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
}
// FirstOrCreateTemplate Insert into database
func (c Core) FirstOrCreateTimeTemplate(b any) error {
return c.store.VqdTimeTemplate().FirstOrCreate(b)
}
// AddVqdTimeTemplate Insert into database
func (c Core) AddVqdTimeTemplate(ctx context.Context, in *AddVqdTimeTemplateInput) (*VqdTimeTemplate, error) {
var out VqdTimeTemplate
if err := copier.Copy(&out, in); err != nil {
slog.Error("Copy", "err", err)
}
if err := c.store.VqdTimeTemplate().Add(ctx, &out); err != nil {
return nil, reason.ErrDB.Withf(`Add err[%s]`, err.Error())
}
return &out, nil
}
// EditVqdTimeTemplate Update object information
func (c Core) EditVqdTimeTemplate(ctx context.Context, in *EditVqdTimeTemplateInput, id int) (*VqdTimeTemplate, error) {
var out VqdTimeTemplate
if err := c.store.VqdTimeTemplate().Edit(ctx, &out, func(b *VqdTimeTemplate) {
if err := copier.Copy(b, in); err != nil {
slog.Error("Copy", "err", err)
}
}, orm.Where("id=?", id)); err != nil {
return nil, reason.ErrDB.Withf(`Edit err[%s]`, err.Error())
}
return &out, nil
}
// DelVqdTimeTemplate Delete object
func (c Core) DelVqdTimeTemplate(ctx context.Context, id int) (*VqdTimeTemplate, error) {
var out VqdTimeTemplate
if err := c.store.VqdTimeTemplate().Del(ctx, &out, orm.Where("id=?", id)); err != nil {
return nil, reason.ErrDB.Withf(`Del err[%s]`, err.Error())
}
return &out, nil
}

View File

@ -0,0 +1,25 @@
// Code generated by gowebx, DO AVOID EDIT.
package vqd
import (
"git.lnton.com/lnton/pkg/web"
)
type FindVqdTimeTemplateInput struct {
web.PagerFilter
Name string `form:"name"` // 名称
}
type EditVqdTimeTemplateInput struct {
Name string `json:"name"`
Plans string `json:"plans"`
Enable bool `json:"enable"`
Des string ` json:"des"` // 描述
}
type AddVqdTimeTemplateInput struct {
Name string `json:"name"`
Plans string `json:"plans"`
Enable bool `json:"enable"`
Des string ` json:"des"` // 描述
}

View File

@ -1,56 +0,0 @@
package vqdsdk
import (
"context"
"easyvqd/internal/conf"
"easyvqd/internal/core/host"
"easyvqd/internal/core/vqd"
"time"
)
type Core struct {
HostCore *host.Core
VqdTaskCore *vqd.Core
Cfg *conf.Bootstrap
}
func NewCore(HostCore *host.Core, VqdTaskCore *vqd.Core, Cfg *conf.Bootstrap) *Core {
core := &Core{
HostCore: HostCore,
VqdTaskCore: VqdTaskCore,
Cfg: Cfg,
}
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 < 40; i++ {
core.VqdTaskCore.AddVqdAlarm(context.TODO(), in)
}
})
// 启用定时清理任务
core.scheduleCleanTask()
// 启用任务管理器
return core
}
//
//func OpenVqdTask(VqdTaskCore *vqd.Core) *Workflow {
// wf := NewWorkflow(WorkflowConfig{
// MaxConcurrency: 100, // 并发
// CleanupInterval: 30 * time.Second, // 每30秒清理一次
// MaxTaskHistory: 500, // 最多保留500个任务历史
// RetentionTime: 60 * time.Second, // 任务保留1分钟
// })
//
// return wf
//}

View File

@ -1 +0,0 @@
package vqdsdk

View File

@ -1,4 +1,4 @@
package vqdsdk package vqdtask
import ( import (
"fmt" "fmt"

View File

@ -0,0 +1,284 @@
package vqdtask
import (
"bufio"
"easyvqd/internal/conf"
"easyvqd/internal/core/host"
"easyvqd/internal/core/vqd"
"easyvqd/pkg/vqdcms"
"fmt"
"log/slog"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
type Core struct {
HostCore *host.Core
VqdTaskCore *vqd.Core
Cfg *conf.Bootstrap
}
var (
VqdTaskMap = vqdcms.VqdTaskMap{M: make(map[string]*vqdcms.VQDHandle)}
)
func NewCore(HostCore *host.Core, VqdTaskCore *vqd.Core, Cfg *conf.Bootstrap) *Core {
core := &Core{
HostCore: HostCore,
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)
}
}
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() {
err := vqdcms.VQDInit()
if err != nil {
slog.Error("vqd cms open", "err", err.Error())
return
}
return
}
func (c Core) UnVqdTask() {
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) 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,
// }
//}
// 测试i帧数据是否可以转为图片文件
func CheckIFramesToJpg() {
dir, _ := os.Getwd()
h264Path := filepath.Join(dir, "check_tip.h264") // 你的H.264裸流文件路径
//1. 检查FFmpeg
ok, err := CheckFFmpeg()
if !ok {
fmt.Println("错误:", err)
os.Exit(1)
}
// 2. 配置参数根据你的H.264文件调整)
imageFormat := "jpg" // 输出图片格式
videoWidth := 0 // 分辨率自动探测如需指定则改为实际值如1920
videoHeight := 0 // 分辨率自动探测如需指定则改为实际值如1080
// 3. 提取H.264关键帧并转图片
if err := ExtractH264KeyFrames(h264Path, dir, imageFormat, videoWidth, videoHeight); err != nil {
fmt.Println("提取H.264关键帧失败:", err)
os.Exit(1)
}
}
// CheckFFmpeg 检查系统是否安装了FFmpeg
func CheckFFmpeg() (bool, error) {
cmd := exec.Command("ffmpeg", "-version")
err := cmd.Run()
if err != nil {
return false, fmt.Errorf("FFmpeg 未安装或未添加到系统PATH: %v", err)
}
return true, nil
}
// ExtractH264KeyFrames 将H.264裸流的关键帧转为图片
// h264Path: H.264裸流文件路径(.264/.h264
// outputDir: 图片输出目录
// format: 输出图片格式 (jpg/png)
// width/height: 视频分辨率若不指定FFmpeg会自动探测
func ExtractH264KeyFrames(h264Path, outputDir, format string, width, height int) error {
// 验证输入文件
if _, err := os.Stat(h264Path); os.IsNotExist(err) {
return fmt.Errorf("H.264文件不存在: %s", h264Path)
}
// 创建输出目录
if err := os.MkdirAll(outputDir, 0755); err != nil {
return fmt.Errorf("创建输出目录失败: %v", err)
}
// 构造FFmpeg命令适配H.264裸流)
outputPattern := filepath.Join(outputDir, "h264_keyframe_%04d."+format)
args := []string{
"-f", "h264", // 明确输入格式为H.264裸流
"-i", h264Path, // 输入H.264裸流文件
"-skip_frame", "nokey", // 只处理关键帧I帧
"-vsync", "0", // 禁用帧同步保证每个I帧都输出
"-q:v", "2", // 图片质量1-31值越小质量越高
}
// 可选指定分辨率如果FFmpeg自动探测失败时使用
if width > 0 && height > 0 {
args = append(args, "-s", fmt.Sprintf("%dx%d", width, height))
}
// 补全输出参数
args = append(args,
"-f", "image2", // 输出图片序列格式
outputPattern, // 输出文件模板
"-y", // 覆盖已存在的文件
)
// 创建命令并捕获输出
cmd := exec.Command("ffmpeg", args...)
_, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("创建标准输出管道失败: %v", err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
return fmt.Errorf("创建标准错误管道失败: %v", err)
}
// 启动命令
if err := cmd.Start(); err != nil {
return fmt.Errorf("启动FFmpeg失败: %v", err)
}
// 实时打印FFmpeg日志便于调试H.264解析问题)
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
line := scanner.Text()
fmt.Println("FFmpeg日志:", line)
// 捕获关键错误信息
if strings.Contains(line, "error") && strings.Contains(line, "H.264") {
fmt.Println("⚠️ H.264解析警告:", line)
}
}
// 等待命令执行完成
if err := cmd.Wait(); err != nil {
return fmt.Errorf("FFmpeg执行失败: %v", err)
}
// 统计输出的关键帧图片数量
files, err := filepath.Glob(filepath.Join(outputDir, "h264_keyframe_*."+format))
if err != nil {
return fmt.Errorf("统计输出图片失败: %v", err)
}
fmt.Printf("成功从H.264裸流提取 %d 个关键帧,保存至: %s\n", len(files), outputDir)
return nil
}

View File

@ -0,0 +1,6 @@
package vqdtask
const (
VIDEO_CODEC_H264 = 0x1C
VIDEO_CODEC_H265 = 0xAE
)

View File

@ -163,9 +163,15 @@ type KV struct {
func InitTemplate(uc *Usecase) error { func InitTemplate(uc *Usecase) error {
cfg := uc.Conf cfg := uc.Conf
in := vqd.VqdTaskTemplate{ inPlan := vqd.VqdTimeTemplate{
Enable: true, Enable: true,
IsDefault: true, IsDefault: true,
}
inTemplate := vqd.VqdTaskTemplate{
Name: "默认",
Des: "默认诊断模板。",
IsDefault: true,
VqdConfig: vqd.VqdConfig{ VqdConfig: vqd.VqdConfig{
Enable: true, Enable: true,
FrmNum: cfg.VqdConfig.FrmNum, FrmNum: cfg.VqdConfig.FrmNum,
@ -230,32 +236,37 @@ func InitTemplate(uc *Usecase) error {
MosaicThr: cfg.VqdFlower.MosaicThr, MosaicThr: cfg.VqdFlower.MosaicThr,
}, },
} }
inTemplate.ID = 1
in.Name = "每天" inTemplate.Model.CreatedAt = orm.Time{Time: time.Now()}
in.Model.ID = 1 if err := uc.VqdTaskCore.FirstOrCreateTaskTemplate(&inTemplate); err != nil {
in.Model.CreatedAt = orm.Time{Time: time.Now()} slog.Error("FirstOrCreateTaskTemplate", "err", err)
in.Plans = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
in.Des = "每天分析,启用全部分析模块。"
if err := uc.VqdTaskCore.FirstOrCreateTemplate(&in); err != nil {
slog.Error("FirstOrCreateTemplate", "err", err)
return err return err
} }
in.Name = "工作日" inPlan.Name = "每天"
in.Model.ID = 2 inPlan.Model.ID = 1
in.Model.CreatedAt = orm.Time{Time: time.Now()} inPlan.Model.CreatedAt = orm.Time{Time: time.Now()}
in.Plans = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000" inPlan.Plans = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
in.Des = "工作日分析,启用全部分析模块。" inPlan.Des = "每天分析"
if err := uc.VqdTaskCore.FirstOrCreateTemplate(&in); err != nil { if err := uc.VqdTaskCore.FirstOrCreateTimeTemplate(&inPlan); err != nil {
slog.Error("FirstOrCreateTemplate", "err", err) slog.Error("FirstOrCreateTimeTemplate", "err", err)
return err return err
} }
in.Name = "双休日" inPlan.Name = "工作日"
in.Model.ID = 3 inPlan.Model.ID = 2
in.Model.CreatedAt = orm.Time{Time: time.Now()} inPlan.Model.CreatedAt = orm.Time{Time: time.Now()}
in.Plans = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111" inPlan.Plans = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000"
in.Des = "休息日分析,启用全部分析模块。" inPlan.Des = "工作日分析"
if err := uc.VqdTaskCore.FirstOrCreateTemplate(&in); err != nil { if err := uc.VqdTaskCore.FirstOrCreateTimeTemplate(&inPlan); err != nil {
slog.Error("FirstOrCreateTemplate", "err", err) slog.Error("FirstOrCreateTimeTemplate", "err", err)
return err
}
inPlan.Name = "双休日"
inPlan.Model.ID = 3
inPlan.Model.CreatedAt = orm.Time{Time: time.Now()}
inPlan.Plans = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111"
inPlan.Des = "休息日分析"
if err := uc.VqdTaskCore.FirstOrCreateTimeTemplate(&inPlan); err != nil {
slog.Error("FirstOrCreateTimeTemplate", "err", err)
return err return err
} }
return nil return nil

View File

@ -5,7 +5,7 @@ import (
"easyvqd/internal/core/media" "easyvqd/internal/core/media"
"easyvqd/internal/core/vqd" "easyvqd/internal/core/vqd"
"easyvqd/internal/core/vqd/store/audioencodedb" "easyvqd/internal/core/vqd/store/audioencodedb"
"easyvqd/internal/core/vqdsdk" "easyvqd/internal/core/vqdtask"
"net/http" "net/http"
"easyvqd/domain/uniqueid" "easyvqd/domain/uniqueid"
@ -41,7 +41,7 @@ type Usecase struct {
VqdTaskAPI VqdTaskAPI VqdTaskAPI VqdTaskAPI
HostCore *host.Core HostCore *host.Core
MediaCore *media.Core MediaCore *media.Core
VqdSdkCore *vqdsdk.Core VqdSdkCore *vqdtask.Core
} }
// NewHTTPHandler 生成Gin框架路由内容 // NewHTTPHandler 生成Gin框架路由内容

View File

@ -37,7 +37,7 @@ func (a VqdTaskAPI) getVqdAlarm(c *gin.Context, _ *struct{}) (any, error) {
ID, _ := strconv.Atoi(c.Param("id")) ID, _ := strconv.Atoi(c.Param("id"))
item, err := a.core.GetVqdAlarm(c.Request.Context(), ID) item, err := a.core.GetVqdAlarm(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
} }
row := make(map[string]interface{}) row := make(map[string]interface{})
@ -61,18 +61,18 @@ func (a VqdTaskAPI) delVqdAlarm(c *gin.Context, _ *struct{}) (any, error) {
ID, _ := strconv.Atoi(c.Param("id")) ID, _ := strconv.Atoi(c.Param("id"))
_, err := a.core.DelVqdAlarm(c.Request.Context(), ID) _, err := a.core.DelVqdAlarm(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqdcms [%d] err [%s]`, ID, err.Error()))
} }
return gin.H{"data": "OK!"}, err return gin.H{"data": "OK!"}, err
} }
func (a VqdTaskAPI) delVqdAlarmAll(c *gin.Context, in *vqd.DelVqdAlarmInput) (any, error) { func (a VqdTaskAPI) delVqdAlarmAll(c *gin.Context, in *vqd.DelVqdAlarmInput) (any, error) {
if len(in.IDs) == 0 { if len(in.IDs) == 0 {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqd ids is empty`)) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqdcms ids is empty`))
} }
_, err := a.core.DelVqdAlarmAll(c.Request.Context(), in.IDs) _, err := a.core.DelVqdAlarmAll(c.Request.Context(), in.IDs)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqd [%d] err [%s]`, in.IDs, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqdcms [%d] err [%s]`, in.IDs, err.Error()))
} }
return gin.H{"data": "OK!"}, err return gin.H{"data": "OK!"}, err
} }

View File

@ -5,7 +5,7 @@ import (
"easyvqd/internal/core/host" "easyvqd/internal/core/host"
"easyvqd/internal/core/media" "easyvqd/internal/core/media"
"easyvqd/internal/core/vqd" "easyvqd/internal/core/vqd"
"easyvqd/internal/core/vqdsdk" "easyvqd/internal/core/vqdtask"
"fmt" "fmt"
"git.lnton.com/lnton/pkg/reason" "git.lnton.com/lnton/pkg/reason"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -16,12 +16,12 @@ import (
type VqdTaskAPI struct { type VqdTaskAPI struct {
core *vqd.Core core *vqd.Core
meidaCore *media.Core meidaCore *media.Core
vqdSdkCore *vqdsdk.Core vqdSdkCore *vqdtask.Core
cfg *conf.Bootstrap cfg *conf.Bootstrap
HostCore *host.Core HostCore *host.Core
} }
func NewVqdTaskAPI(core *vqd.Core, meidaCore *media.Core, vqdSdkCore *vqdsdk.Core, HostCore *host.Core, cfg *conf.Bootstrap) VqdTaskAPI { func NewVqdTaskAPI(core *vqd.Core, meidaCore *media.Core, vqdSdkCore *vqdtask.Core, HostCore *host.Core, cfg *conf.Bootstrap) VqdTaskAPI {
return VqdTaskAPI{core: core, meidaCore: meidaCore, vqdSdkCore: vqdSdkCore, HostCore: HostCore, cfg: cfg} return VqdTaskAPI{core: core, meidaCore: meidaCore, vqdSdkCore: vqdSdkCore, HostCore: HostCore, cfg: cfg}
} }
@ -42,6 +42,13 @@ func RegisterVqdTask(g gin.IRouter, api VqdTaskAPI, handler ...gin.HandlerFunc)
groupTemplate.POST("", web.WarpH(api.addVqdTaskTemplate)) groupTemplate.POST("", web.WarpH(api.addVqdTaskTemplate))
groupTemplate.DELETE("/:id", web.WarpH(api.delVqdTaskTemplate)) groupTemplate.DELETE("/:id", web.WarpH(api.delVqdTaskTemplate))
groupTime := g.Group("/api/plan", handler...)
groupTime.GET("", web.WarpH(api.findVqdTimeTemplate))
groupTime.GET("/:id", web.WarpH(api.getVqdTimeTemplate))
groupTime.PUT("/:id", web.WarpH(api.editVqdTimeTemplate))
groupTime.POST("", web.WarpH(api.addVqdTimeTemplate))
groupTime.DELETE("/:id", web.WarpH(api.delVqdTimeTemplate))
groupAlarm := g.Group("/api/alarm", handler...) groupAlarm := g.Group("/api/alarm", handler...)
groupAlarm.GET("", web.WarpH(api.findVqdAlarm)) groupAlarm.GET("", web.WarpH(api.findVqdAlarm))
groupAlarm.GET("/:id", web.WarpH(api.getVqdAlarm)) groupAlarm.GET("/:id", web.WarpH(api.getVqdAlarm))
@ -60,14 +67,21 @@ func (a VqdTaskAPI) findVqdTask(c *gin.Context, in *vqd.FindVqdTaskInput) (any,
row := make(map[string]interface{}) row := make(map[string]interface{})
row["name"] = item.Name row["name"] = item.Name
row["id"] = item.ID row["id"] = item.ID
row["enable"] = item.Enable
row["channel_id"] = item.ChannelID row["channel_id"] = item.ChannelID
row["channel_name"] = item.ChannelName row["channel_name"] = item.ChannelName
row["task_template_id"] = item.TaskTemplateID row["task_template_id"] = item.TaskTemplateID
row["task_template_name"] = item.TaskTemplateName row["time_template_id"] = item.TimeTemplateID
row["task_template_name"] = ""
row["time_template_name"] = ""
template, errs := a.core.GetIDVqdTaskTemplate(c.Request.Context(), item.TaskTemplateID) template, errs := a.core.GetIDVqdTaskTemplate(c.Request.Context(), item.TaskTemplateID)
if errs == nil && template != nil { if errs == nil && template != nil {
row["task_template_name"] = template.Name row["task_template_name"] = template.Name
} }
timeTemplate, errs := a.core.GetIDVqdTimeTemplate(c.Request.Context(), item.TimeTemplateID)
if errs == nil && template != nil {
row["time_template_name"] = timeTemplate.Name
}
row["created_at"] = item.CreatedAt row["created_at"] = item.CreatedAt
row["updated_at"] = item.UpdatedAt row["updated_at"] = item.UpdatedAt
@ -80,16 +94,27 @@ func (a VqdTaskAPI) getVqdTask(c *gin.Context, _ *struct{}) (any, error) {
ID, _ := strconv.Atoi(c.Param("id")) ID, _ := strconv.Atoi(c.Param("id"))
item, err := a.core.GetVqdTask(c.Request.Context(), ID) item, err := a.core.GetVqdTask(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
} }
row := make(map[string]interface{}) row := make(map[string]interface{})
row["name"] = item.Name row["name"] = item.Name
row["id"] = item.ID row["id"] = item.ID
row["enable"] = item.Enable
row["channel_id"] = item.ChannelID row["channel_id"] = item.ChannelID
row["channel_name"] = item.ChannelName row["channel_name"] = item.ChannelName
row["task_template_id"] = item.TaskTemplateID row["task_template_id"] = item.TaskTemplateID
row["task_template_name"] = item.TaskTemplateName row["time_template_id"] = item.TimeTemplateID
row["task_template_name"] = ""
row["time_template_name"] = ""
template, errs := a.core.GetIDVqdTaskTemplate(c.Request.Context(), item.TaskTemplateID)
if errs == nil && template != nil {
row["task_template_name"] = template.Name
}
timeTemplate, errs := a.core.GetIDVqdTimeTemplate(c.Request.Context(), item.TimeTemplateID)
if errs == nil && template != nil {
row["time_template_name"] = timeTemplate.Name
}
row["created_at"] = item.CreatedAt row["created_at"] = item.CreatedAt
row["updated_at"] = item.UpdatedAt row["updated_at"] = item.UpdatedAt
@ -99,7 +124,7 @@ func (a VqdTaskAPI) getVqdTask(c *gin.Context, _ *struct{}) (any, error) {
func (a VqdTaskAPI) addVqdTask(c *gin.Context, in *vqd.AddVqdTaskInput) (any, error) { func (a VqdTaskAPI) addVqdTask(c *gin.Context, in *vqd.AddVqdTaskInput) (any, error) {
_, err := a.core.AddVqdTask(c.Request.Context(), in) _, err := a.core.AddVqdTask(c.Request.Context(), in)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add vqd err [%s]`, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add vqdcms err [%s]`, err.Error()))
} }
return gin.H{"data": "OK!"}, err return gin.H{"data": "OK!"}, err
@ -109,12 +134,12 @@ func (a VqdTaskAPI) editVqdTask(c *gin.Context, in *vqd.EditVqdTaskInput) (any,
ID, _ := strconv.Atoi(c.Param("id")) ID, _ := strconv.Atoi(c.Param("id"))
_, err := a.core.GetVqdTask(c.Request.Context(), ID) _, err := a.core.GetVqdTask(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
} }
_, err = a.core.EditVqdTask(c.Request.Context(), in, ID) _, err = a.core.EditVqdTask(c.Request.Context(), in, ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`edit vqd err [%s]`, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`edit vqdcms err [%s]`, err.Error()))
} }
return gin.H{"data": "OK!"}, err return gin.H{"data": "OK!"}, err
} }
@ -123,7 +148,7 @@ func (a VqdTaskAPI) delVqdTask(c *gin.Context, _ *struct{}) (any, error) {
ID, _ := strconv.Atoi(c.Param("id")) ID, _ := strconv.Atoi(c.Param("id"))
_, err := a.core.DelVqdTask(c.Request.Context(), ID) _, err := a.core.DelVqdTask(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqdcms [%d] err [%s]`, ID, err.Error()))
} }
return gin.H{"data": "OK!"}, err return gin.H{"data": "OK!"}, err

View File

@ -18,8 +18,6 @@ func (a VqdTaskAPI) findVqdTaskTemplate(c *gin.Context, in *vqd.FindVqdTaskTempl
row["id"] = item.ID row["id"] = item.ID
row["name"] = item.Name row["name"] = item.Name
row["des"] = item.Des row["des"] = item.Des
row["plans"] = item.Plans
row["enable"] = item.Enable
row["is_default"] = item.IsDefault row["is_default"] = item.IsDefault
row["vqd_config"] = item.VqdConfig row["vqd_config"] = item.VqdConfig
row["vqd_lgt_dark"] = item.VqdLgtDark row["vqd_lgt_dark"] = item.VqdLgtDark
@ -45,7 +43,7 @@ func (a VqdTaskAPI) getVqdTaskTemplate(c *gin.Context, _ *struct{}) (any, error)
ID, _ := strconv.Atoi(c.Param("id")) ID, _ := strconv.Atoi(c.Param("id"))
item, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID) item, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
} }
row := make(map[string]interface{}) row := make(map[string]interface{})
@ -65,8 +63,6 @@ func (a VqdTaskAPI) getVqdTaskTemplate(c *gin.Context, _ *struct{}) (any, error)
row["vqd_mosaic"] = item.VqdMosaic row["vqd_mosaic"] = item.VqdMosaic
row["vqd_flower"] = item.VqdFlower row["vqd_flower"] = item.VqdFlower
row["des"] = item.Des row["des"] = item.Des
row["plans"] = item.Plans
row["enable"] = item.Enable
row["created_at"] = item.CreatedAt row["created_at"] = item.CreatedAt
row["updated_at"] = item.UpdatedAt row["updated_at"] = item.UpdatedAt
@ -75,7 +71,7 @@ func (a VqdTaskAPI) getVqdTaskTemplate(c *gin.Context, _ *struct{}) (any, error)
func (a VqdTaskAPI) addVqdTaskTemplate(c *gin.Context, in *vqd.AddVqdTaskTemplateInput) (any, error) { func (a VqdTaskAPI) addVqdTaskTemplate(c *gin.Context, in *vqd.AddVqdTaskTemplateInput) (any, error) {
_, err := a.core.AddVqdTaskTemplate(c.Request.Context(), in) _, err := a.core.AddVqdTaskTemplate(c.Request.Context(), in)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add vqd err [%s]`, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add vqdcms err [%s]`, err.Error()))
} }
return gin.H{"data": "OK!"}, err return gin.H{"data": "OK!"}, err
@ -85,12 +81,12 @@ func (a VqdTaskAPI) editVqdTaskTemplate(c *gin.Context, in *vqd.EditVqdTaskTempl
ID, _ := strconv.Atoi(c.Param("id")) ID, _ := strconv.Atoi(c.Param("id"))
_, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID) _, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
} }
_, err = a.core.EditVqdTaskTemplate(c.Request.Context(), in, ID) _, err = a.core.EditVqdTaskTemplate(c.Request.Context(), in, ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`edit vqd err [%s]`, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`edit vqdcms err [%s]`, err.Error()))
} }
return gin.H{"data": "OK!"}, err return gin.H{"data": "OK!"}, err
} }
@ -99,7 +95,7 @@ func (a VqdTaskAPI) delVqdTaskTemplate(c *gin.Context, _ *struct{}) (any, error)
ID, _ := strconv.Atoi(c.Param("id")) ID, _ := strconv.Atoi(c.Param("id"))
info, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID) info, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
} }
if info.IsDefault { if info.IsDefault {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`默认模板不支持删除 [%s] `, info.Name)) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`默认模板不支持删除 [%s] `, info.Name))
@ -112,7 +108,7 @@ func (a VqdTaskAPI) delVqdTaskTemplate(c *gin.Context, _ *struct{}) (any, error)
} }
_, err = a.core.DelVqdTaskTemplate(c.Request.Context(), ID) _, err = a.core.DelVqdTaskTemplate(c.Request.Context(), ID)
if err != nil { if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqd [%d] err [%s]`, ID, err.Error())) return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqdcms [%d] err [%s]`, ID, err.Error()))
} }
return gin.H{"data": "OK!"}, err return gin.H{"data": "OK!"}, err

View File

@ -0,0 +1,95 @@
package api
import (
"easyvqd/internal/core/vqd"
"fmt"
"git.lnton.com/lnton/pkg/reason"
"github.com/gin-gonic/gin"
"strconv"
)
func (a VqdTaskAPI) findVqdTimeTemplate(c *gin.Context, in *vqd.FindVqdTimeTemplateInput) (any, error) {
items, total, err := a.core.FindVqdTimeTemplate(c.Request.Context(), in)
rows := make([]map[string]interface{}, 0)
for _, item := range items {
//row := structs.Map(item)
row := make(map[string]interface{})
row["id"] = item.ID
row["name"] = item.Name
row["des"] = item.Des
row["plans"] = item.Plans
row["enable"] = item.Enable
row["is_default"] = item.IsDefault
row["created_at"] = item.CreatedAt
row["updated_at"] = item.UpdatedAt
rows = append(rows, row)
}
return gin.H{"items": rows, "total": total}, err
}
func (a VqdTaskAPI) getVqdTimeTemplate(c *gin.Context, _ *struct{}) (any, error) {
ID, _ := strconv.Atoi(c.Param("id"))
item, 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()))
}
row := make(map[string]interface{})
row["id"] = item.ID
row["name"] = item.Name
row["is_default"] = item.IsDefault
row["des"] = item.Des
row["plans"] = item.Plans
row["enable"] = item.Enable
row["created_at"] = item.CreatedAt
row["updated_at"] = item.UpdatedAt
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)
if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add vqdcms err [%s]`, err.Error()))
}
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)
if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqdcms [%d] err [%s]`, ID, err.Error()))
}
_, err = a.core.EditVqdTimeTemplate(c.Request.Context(), in, ID)
if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`edit vqdcms err [%s]`, err.Error()))
}
return gin.H{"data": "OK!"}, err
}
func (a VqdTaskAPI) delVqdTimeTemplate(c *gin.Context, _ *struct{}) (any, error) {
ID, _ := strconv.Atoi(c.Param("id"))
info, 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()))
}
if info.IsDefault {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`默认模板不支持删除 [%s] `, info.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()))
}
return gin.H{"data": "OK!"}, err
}

121
pkg/decoder/core.go Normal file
View File

@ -0,0 +1,121 @@
package decoder
/*
#cgo CFLAGS: -w -I${SRCDIR}/include -fPIC
#cgo CPPFLAGS: -w -I${SRCDIR}/include -fPIC
#cgo windows LDFLAGS: -L${SRCDIR}/include -L${SRCDIR}/../ -lez_decoder
#cgo linux LDFLAGS: -Wl,-rpath=./ -L${SRCDIR}/include/linux -L${SRCDIR}/include/RTSP/lib -lez_decoder -lavfilter -lavformat -lavcodec -lavdevice -lswscale -lpostproc -lswresample -lavutil -lx264 -lm -ldl -lstdc++ -pthread -lrt -lssl -lcrypto -lz -lbz2
#include "ez_decoder.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void CToBytes(unsigned char *dst, unsigned char *src, int src_len)
{
//memset(dst, 0, src_len);
memcpy(dst, src, src_len);
}
*/
import "C"
import (
"fmt"
"log/slog"
"os"
"sync"
"unsafe"
)
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 {
v.Lock()
defer v.Unlock()
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")
}
func (v *VideoDecoder) Destroy() error {
v.Lock()
defer v.Unlock()
if !v.IsStart {
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) {
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))
}
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))
}
//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)
//C.CToBytes((*C.uchar)(unsafe.Pointer(&_data[0])), out_data, C.int(data_size))
//return _w, _h, _data, nil
}
return 0, 0, nil, fmt.Errorf("ez_decoder_push_data fail %d", ret)
}
func IsReturnErrorf(ret int, str string) error {
if int(ret) != 0 {
return fmt.Errorf("%s error:%d", str, ret)
}
return nil
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,90 @@
/*
Copyright (c) 2013-2015 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
*/
#ifndef _EASY_STREAM_CLIENT_API_H
#define _EASY_STREAM_CLIENT_API_H
#include "EasyTypes.h"
/* 推送事件类型定义 */
typedef enum __EASY_STREAM_CLIENT_STATE_T
{
EASY_STREAM_CLIENT_STATE_CONNECTING = 1, /* 连接中 */
EASY_STREAM_CLIENT_STATE_CONNECTED, /* 连接成功 */
EASY_STREAM_CLIENT_STATE_CONNECT_FAILED, /* 连接失败 */
EASY_STREAM_CLIENT_STATE_CONNECT_ABORT, /* 连接中断 */
EASY_STREAM_CLIENT_STATE_PUSHING, /* 推流中 */
EASY_STREAM_CLIENT_STATE_DISCONNECTED, /* 断开连接 */
EASY_STREAM_CLIENT_STATE_EXIT, /* 退出连接 */
EASY_STREAM_CLIENT_STATE_ERROR
} EASY_STREAM_CLIENT_STATE_T;
/*
_channelPtr: ,
_frameType: EASY_SDK_VIDEO_FRAME_FLAG/EASY_SDK_AUDIO_FRAME_FLAG/EASY_SDK_EVENT_FRAME_FLAG/...
_pBuf: Demo
_frameInfo:
*/
typedef int (Easy_APICALL *EasyStreamClientCallBack)(void *_channelPtr, int _frameType, void *pBuf, EASY_FRAME_INFO* _frameInfo);
typedef int (Easy_APICALL *EasyDownloadCallBack)(void *userptr, const char* path);
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef ANDROID
Easy_API Easy_I32 Easy_APICALL EasyStreamClient_Activate(char *license, char* userPtr);
#else
Easy_API Easy_I32 Easy_APICALL EasyStreamClient_Activate(char *license);
#endif
/* 创建EasyStreamClient句柄 返回0表示成功返回非0表示失败 ; loglevel : 0 - quiet 1 - debug*/
Easy_API int Easy_APICALL EasyStreamClient_Init(Easy_Handle *handle, int loglevel);
/* 释放EasyStreamClient 参数为EasyStreamClient句柄 */
Easy_API int Easy_APICALL EasyStreamClient_Deinit(Easy_Handle handle);
/* 设置背景音 flag: 1 开启 0:关闭 url:背景音路径包含文件名 ret: 0成功 < 0配置失败 1:不支持背景音功能*/
Easy_API int Easy_APICALL EasyStreamClient_SetBackAudio(Easy_Handle handle, int flag, char* url);
/* 设置数据回调 */
Easy_API int Easy_APICALL EasyStreamClient_SetCallback(Easy_Handle handle, EasyStreamClientCallBack callback);
/* 设置叠加图片 */
Easy_API int Easy_APICALL EasyStreamClient_SetOverlayImage(Easy_Handle handle, int left, int top, int scaleWidth, int scaleHeight, const char* imageFilePath);
/* 打开网络流 */
Easy_API int Easy_APICALL EasyStreamClient_OpenStream(Easy_Handle handle, char *url, EASY_RTP_CONNECT_TYPE connType, void *userPtr, int reconn, int timeout, int useExtraData);
/* 获取输入流的context */
Easy_API int Easy_APICALL EasyStreamClient_GetStreamContext(Easy_Handle handle, void** avFormatContext, void** avCodecContext);
/* 获取快照 */
Easy_API int Easy_APICALL EasyStreamClient_GetSnap(Easy_Handle handle);
Easy_API int Easy_APICALL EasyStreamClient_ConvertFrame2Image(const unsigned int videoCodec, const unsigned char* keyFrameData, int keyFrameDataSize, unsigned char** outImage, int* outImageSize);
Easy_API int Easy_APICALL EasyStreamClient_ReleaseImageData(unsigned char** imageData);
/* 设置音频是否启用 */
Easy_API int Easy_APICALL EasyStreamClient_SetAudioEnable(Easy_Handle handle, int enable);
Easy_API int Easy_APICALL EasyStreamClient_SetAudioOutFormat(Easy_Handle handle, unsigned int audioFormat, int samplerate, int channels);
/* 获取音频是否启用 */
Easy_API int Easy_APICALL EasyStreamClient_GetAudioEnable(Easy_Handle handle);
/*录像下载*/
Easy_API int Easy_APICALL EasyStreamClient_SetDownloadCallback(EasyDownloadCallBack callback);
Easy_API int Easy_APICALL EasyStreamClient_DownloadOneRecord(const char* rootPath, const char* streamName, const char* startTime, void* userPtr);
Easy_API int Easy_APICALL EasyStreamClient_DownloadPeriod(const char* rootPath, const char* streamName, const char* startTime, const char* endTime, void* userPtr);
#ifdef __cplusplus
};
#endif
#endif

View File

@ -0,0 +1,167 @@
/*
Copyright (c) 2012-2019 TSINGSEE.COM. All rights reserved.
Github: https://github.com/tsingsee
WEChat: tsingsee
Website: http://www.tsingsee.com
*/
#ifndef _Easy_Types_H
#define _Easy_Types_H
#ifdef _WIN32
#define Easy_API __declspec(dllexport)
#define Easy_APICALL __stdcall
#define WIN32_LEAN_AND_MEAN
#else
#define Easy_API __attribute__ ((visibility("default")))
#define Easy_APICALL __attribute__ ((visibility("default")))
#endif
#define Easy_Handle void*
typedef int Easy_I32;
typedef unsigned char Easy_U8;
typedef unsigned char Easy_UChar;
typedef unsigned short Easy_U16;
typedef unsigned int Easy_U32;
typedef unsigned char Easy_Bool;
enum
{
Easy_NoErr = 0,
Easy_RequestFailed = -1,
Easy_Unimplemented = -2,
Easy_RequestArrived = -3,
Easy_OutOfState = -4,
Easy_NotAModule = -5,
Easy_WrongVersion = -6,
Easy_IllegalService = -7,
Easy_BadIndex = -8,
Easy_ValueNotFound = -9,
Easy_BadArgument = -10,
Easy_ReadOnly = -11,
Easy_NotPreemptiveSafe = -12,
Easy_NotEnoughSpace = -13,
Easy_WouldBlock = -14,
Easy_NotConnected = -15,
Easy_FileNotFound = -16,
Easy_NoMoreData = -17,
Easy_AttrDoesntExist = -18,
Easy_AttrNameExists = -19,
Easy_InstanceAttrsNotAllowed = -20,
Easy_InvalidSocket = -21,
Easy_MallocError = -22,
Easy_ConnectError = -23,
Easy_SendError = -24
};
typedef int Easy_Error;
typedef enum __EASY_ACTIVATE_ERR_CODE_ENUM
{
EASY_ACTIVATE_INVALID_KEY = -1, /* 无效Key */
EASY_ACTIVATE_TIME_ERR = -2, /* 时间错误 */
EASY_ACTIVATE_PROCESS_NAME_LEN_ERR = -3, /* 进程名称长度不匹配 */
EASY_ACTIVATE_PROCESS_NAME_ERR = -4, /* 进程名称不匹配 */
EASY_ACTIVATE_VALIDITY_PERIOD_ERR= -5, /* 有效期校验不一致 */
EASY_ACTIVATE_PLATFORM_ERR = -6, /* 平台不匹配 */
EASY_ACTIVATE_COMPANY_ID_LEN_ERR= -7, /* 授权使用商不匹配 */
EASY_ACTIVATE_SUCCESS = 9999, /* 激活成功 */
}EASY_ACTIVATE_ERR_CODE_ENUM;
/* 视频编码 */
#define EASY_SDK_VIDEO_CODEC_H264 0x1C /* H264 */
#define EASY_SDK_VIDEO_CODEC_H265 0xAE /* H265 */
#define EASY_SDK_VIDEO_CODEC_MJPEG 0x08 /* MJPEG */
#define EASY_SDK_VIDEO_CODEC_MPEG4 0x0D /* MPEG4 */
/* 音频编码 */
#define EASY_SDK_AUDIO_CODEC_AAC 0x15002 /* AAC */
#define EASY_SDK_AUDIO_CODEC_G711U 0x10006 /* G711 ulaw*/
#define EASY_SDK_AUDIO_CODEC_G711A 0x10007 /* G711 alaw*/
#define EASY_SDK_AUDIO_CODEC_G726 0x1100B /* G726 */
#define EASY_SDK_EVENT_CODEC_ERROR 0x63657272 /* ERROR */
#define EASY_SDK_EVENT_CODEC_EXIT 0x65786974 /* EXIT */
/* 音视频帧标识 */
#define EASY_SDK_VIDEO_FRAME_FLAG 0x00000001 /* 视频帧标志 */
#define EASY_SDK_AUDIO_FRAME_FLAG 0x00000002 /* 音频帧标志 */
#define EASY_SDK_EVENT_FRAME_FLAG 0x00000004 /* 事件帧标志 */
#define EASY_SDK_RTP_FRAME_FLAG 0x00000008 /* RTP帧标志 */
#define EASY_SDK_SDP_FRAME_FLAG 0x00000010 /* SDP帧标志 */
#define EASY_SDK_MEDIA_INFO_FLAG 0x00000020 /* 媒体类型标志*/
#define EASY_SDK_SNAP_FRAME_FLAG 0x00000040 /* 图片标志*/
/* 视频关键字标识 */
#define EASY_SDK_VIDEO_FRAME_I 0x01 /* I帧 */
#define EASY_SDK_VIDEO_FRAME_P 0x02 /* P帧 */
#define EASY_SDK_VIDEO_FRAME_B 0x03 /* B帧 */
#define EASY_SDK_VIDEO_FRAME_J 0x04 /* JPEG */
/* 连接类型 */
typedef enum __EASY_RTP_CONNECT_TYPE
{
EASY_RTP_OVER_TCP = 0x01, /* RTP Over TCP */
EASY_RTP_OVER_UDP, /* RTP Over UDP */
EASY_RTP_OVER_MULTICAST /* RTP Over MULTICAST */
}EASY_RTP_CONNECT_TYPE;
typedef struct __EASY_AV_Frame
{
Easy_U32 u32AVFrameFlag; /* 帧标志 视频 or 音频 */
Easy_U32 u32AVFrameLen; /* 帧的长度 */
Easy_U32 u32VFrameType; /* 视频的类型I帧或P帧 */
Easy_U8 *pBuffer; /* 数据 */
Easy_U32 u32TimestampSec; /* 时间戳(秒)*/
Easy_U32 u32TimestampUsec; /* 时间戳(微秒) */
Easy_U32 u32PTS;
} EASY_AV_Frame;
/* 媒体信息 */
typedef struct __EASY_MEDIA_INFO_T
{
Easy_U32 u32VideoCodec; /* 视频编码类型 */
Easy_U32 u32VideoFps; /* 视频帧率 */
Easy_U32 u32AudioCodec; /* 音频编码类型 */
Easy_U32 u32AudioSamplerate; /* 音频采样率 */
Easy_U32 u32AudioChannel; /* 音频通道数 */
Easy_U32 u32AudioBitsPerSample; /* 音频采样精度 */
Easy_U32 u32VpsLength;
Easy_U32 u32SpsLength;
Easy_U32 u32PpsLength;
Easy_U32 u32SeiLength;
Easy_U8 u8Vps[256];
Easy_U8 u8Sps[256];
Easy_U8 u8Pps[128];
Easy_U8 u8Sei[128];
}EASY_MEDIA_INFO_T;
/* 帧信息 */
typedef struct
{
unsigned int codec; /* 音视频格式 */
unsigned int type; /* 视频帧类型 */
unsigned char fps; /* 视频帧率 */
unsigned short width; /* 视频宽 */
unsigned short height; /* 视频高 */
unsigned int reserved1; /* 保留参数1 */
unsigned int reserved2; /* 保留参数2 */
unsigned int sample_rate; /* 音频采样率 */
unsigned int channels; /* 音频声道数 */
unsigned int bits_per_sample; /* 音频采样精度 */
unsigned int length; /* 音视频帧大小 */
unsigned int timestamp_usec; /* 时间戳,微妙 */
unsigned int timestamp_sec; /* 时间戳 秒 */
unsigned int pts;
float bitrate; /* 比特率 */
float losspacket; /* 丢包率 */
}EASY_FRAME_INFO;
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
#pragma once #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 #define EZ_DECODER_EXPORTS_API __declspec(dllexport) #define EZ_DECODER_EXPORTS_APICALL __stdcall #else #define EZ_DECODER_EXPORTS_API #define EZ_DECODER_EXPORTS_APICALL #endif typedef void* EZ_DECODER_HANDLE; typedef struct __EZ_DECODER_INFO__ { int width; int height; int data_size; }EZDecoderInfo; EZ_DECODER_EXPORTS_API int EZ_DECODER_EXPORTS_APICALL ez_decoder_create(EZ_DECODER_HANDLE *handle); EZ_DECODER_EXPORTS_API int EZ_DECODER_EXPORTS_APICALL ez_decoder_destroy(EZ_DECODER_HANDLE* handle); EZ_DECODER_EXPORTS_API int EZ_DECODER_EXPORTS_APICALL ez_decoder_push_data(EZ_DECODER_HANDLE handle, const unsigned char *data, int data_size, int video_id, unsigned char **out_data, EZDecoderInfo *info); /* <20><><EFBFBD><EFBFBD>־ */ EZ_DECODER_EXPORTS_API int EZ_DECODER_EXPORTS_APICALL ez_activate_log(const char* logDir, int logDirSize); //typedef void* PullStream_Handle; #define PullStream_Handle void* typedef int (EZ_DECODER_EXPORTS_APICALL* PullStreamCallBack)(void* _channelPtr, int _frameType, void* pBuf, int puf_len, int frameCodec, int frameFlag); EZ_DECODER_EXPORTS_API int EZ_DECODER_EXPORTS_APICALL PullStream_Init(PullStream_Handle* handle); EZ_DECODER_EXPORTS_API int EZ_DECODER_EXPORTS_APICALL PullStream_DeInit(PullStream_Handle handle); EZ_DECODER_EXPORTS_API int EZ_DECODER_EXPORTS_APICALL PullStream_SetCallback(PullStream_Handle handle, PullStreamCallBack callback); EZ_DECODER_EXPORTS_API int EZ_DECODER_EXPORTS_APICALL PullStream_Open(PullStream_Handle handle, void* userPtr, char *url, int timeout); #ifdef __cplusplus } #endif

Binary file not shown.

32
pkg/vqdcms/core.go Normal file
View File

@ -0,0 +1,32 @@
package vqdcms
import (
"sync"
)
type VqdTaskMap struct {
M map[string]*VQDHandle // 自定义K-V的类型;
sync.RWMutex
}
// 读出相应Key的子Map
func (m *VqdTaskMap) LoadTaskMap(key string) (value *VQDHandle, ok bool) {
m.RLock()
defer m.RUnlock()
value, ok = m.M[key]
return
}
// 删除相应Key的子Map
func (m *VqdTaskMap) DeleteTaskMap(key string) {
m.Lock()
defer m.Unlock()
delete(m.M, key)
}
// 增加或修改相应Key的子Map
func (m *VqdTaskMap) StoreChildMap(key string, value *VQDHandle) {
m.Lock()
defer m.Unlock()
m.M[key] = value
}

View File

@ -0,0 +1,73 @@
#pragma once
#include "nxu_vqd_api.h"
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef _WIN32
#define Easy_NXU_EXPORTS_API __declspec(dllexport)
#define Easy_NXU_EXPORTS_APICALL __stdcall
#else
#define Easy_NXU_EXPORTS_API
#define Easy_NXU_EXPORTS_APICALL
#endif
typedef struct __tagVideoInfo__
{
NXU_HANDLE phVQD;
NXU_HANDLE VideoHandle;
int interval;
NXU_VQD_Para_S stPara;
char url[256];
} VideoInfo;
// <20><>ȡ<EFBFBD>
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_vqd_version(char* version);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_vqd_create(VideoInfo *handle);
// ע<><D7A2><EFBFBD><EFBFBD><EFBFBD>
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_vqd_destroy(VideoInfo *handle);
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_vqd_run(VideoInfo handle, int frameNum, char *save_filename, int save_filename_size, NXU_VQD_Result_S *out_stResult);
/*typedef struct __tagVideoInfoCVR__
{
NXU_HANDLE phVQD;
int interval;
NXU_VQD_Para_S stPara;
int width;
int height;
} VideoInfoCVR;*/
typedef void* VideoInfoCVR;
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_cvr_vqd_load(char *path, int path_size, char* errmsg);
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_cvr_vqd_unload();
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_cvr_vqd_create(VideoInfoCVR* handle);
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_cvr_vqd_destroy(VideoInfoCVR* handle);
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_cvr_vqd_config(VideoInfoCVR handle, NXU_VQD_Para_S *stPara, int interval);
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_cvr_vqd_run(VideoInfoCVR handle, int width, int height, char* data, int frameNum, char* save_filename, int save_filename_size, NXU_VQD_Result_S* out_stResult);
Easy_NXU_EXPORTS_API int Easy_NXU_EXPORTS_APICALL
ez_cvr_vqd_img(VideoInfoCVR handle, char* jpg_path, int jpg_path_size, NXU_VQD_Result_S* out_stResult);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,597 @@
/************************************************************************
* Copyright (C), 2023 NXU Network Tech. Co., Ltd.
* LET AI CONNECT FUTURE
*************************************************************************
*
* File Name : nxu_common.h
* Version : 1.0.0
* Author : 007 @ TECH TEAM (zhou24388@163.com)
* Created : 2023/06/12
* Description : The common data type defination
******************************************************************************/
#ifndef __NXU_COMMON_H__
#define __NXU_COMMON_H__
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef NULL
#define NULL 0L
#endif
#define NXU_NULL 0L
#define NXU_NULL_PTR 0L
#define NXU_MAX(a, b) ((a) > (b) ? (a) : (b))
#define NXU_MIN(a, b) ((a) < (b) ? (a) : (b))
#define NXU_ABS(a) ((a) >= 0 ? (a) : (-(a)))
/* Clip Data between min to max*/
#define NXU_CLIP(val, min_val, max_val) ((val) >= (min_val) ? ((val) <= (max_val) ? (val) : (max_val)) : (min_val))
#define NXU_ASSERT(a) assert(a)
/** NXU_EXPORTS */
#if defined(NXU_API_EXPORTS)
#define NXU_EXPORTS __declspec(dllexport)
#elif defined(NXU_API_IMPORTS)
#define NXU_EXPORTS __declspec(dllimport)
#else
#define NXU_EXPORTS extern
#endif
/** NXU_INLINE */
#ifndef NXU_INLINE
#if defined __cplusplus
#define NXU_INLINE inline
#elif (defined WIN32 || defined WIN64) && !defined __GNUC__
#define NXU_INLINE __inline
#else
#define NXU_INLINE static
#endif
#endif
/* META Type Definitions */
typedef unsigned char NXU_U8;
typedef unsigned char NXU_UCHAR;
typedef unsigned short NXU_U16;
typedef unsigned int NXU_U32;
typedef char NXU_S8;
typedef char NXU_CHAR;
typedef char * NXU_PCHAR;
typedef short NXU_S16;
typedef int NXU_S32;
#ifndef _M_IX86
typedef unsigned long long NXU_U64;
typedef long long NXU_S64;
#else
typedef __int64 NXU_U64;
typedef __int64 NXU_S64;
#endif
typedef float NXU_FLOAT;
typedef double NXU_DOUBLE;
typedef unsigned long NXU_SIZE_T;
typedef unsigned long NXU_LENGTH_T;
/* Handle */
typedef void NXU_VOID;
typedef void * NXU_HANDLE;
typedef void * NXU_PVOID;
/* ------------------ Fixed Point Type ------------------------*/
/* u8bit */
typedef unsigned char NXU_U0Q8;
typedef unsigned char NXU_U1Q7;
typedef unsigned char NXU_U5Q3;
/* u16bit */
typedef unsigned short NXU_U0Q16;
typedef unsigned short NXU_U4Q12;
typedef unsigned short NXU_U5Q11;
typedef unsigned short NXU_U6Q9;
typedef unsigned short NXU_U8Q8;
typedef unsigned short NXU_U9Q7;
typedef unsigned short NXU_U12Q4;
typedef unsigned short NXU_U14Q2;
/* s16bit */
typedef short NXU_S9Q7;
typedef short NXU_S14Q2;
typedef short NXU_S1Q15;
/* u32bit */
typedef unsigned int NXU_U22Q10;
typedef unsigned int NXU_U25Q7;
typedef unsigned int NXU_U21Q11;
/* s32bit */
typedef int NXU_S25Q7;
typedef int NXU_S16Q16;
/* Boolean */
typedef enum _nxubool_e_
{
NXU_FALSE = 0,
NXU_TRUE = 1,
} NXU_BOOL;
/* Shape Enum */
typedef enum _nxushape_e_
{
NXU_SHAPE_POINT = 0, /* Point */
NXU_SHAPE_LINE = 1, /* Line */
NXU_SHAPE_RECT = 2, /* Rect */
NXU_SHAPE_CIRCLE = 3, /* Circle */
NXU_SHAPE_ELLIPSE = 4, /* Ellipse */
NXU_SHAPE_POLYGON = 5, /* Polygon */
NXU_SHAPE_POLYLINE = 6, /* Polyline */
NXU_SHAPE_FREEHAND = 7 /* Free */
} NXU_SHAPE_E;
/************************************************************************/
/* Point definition */
/************************************************************************/
/* 2D Point Definition */
typedef struct _nxu2dpoint_s_
{
NXU_S32 s32X; /* X */
NXU_S32 s32Y; /* Y */
} NXU_POINT_S, NXU_2DPOINT_S;
/* 2D Point with float data */
typedef struct _nxu2dpoint32f_s_
{
NXU_FLOAT f32X; /* X */
NXU_FLOAT f32Y; /* Y */
} NXU_POINT32F_S, NXU_2DPOINT32F_S;
/* 3D Point */
typedef struct _nxu3dpoint_s_
{
NXU_S32 s32X; /* X */
NXU_S32 s32Y; /* Y */
NXU_S32 s32Z; /* Z */
} NXU_3DPOINT_S;
/* 3D Point with float data */
typedef struct _nxu3dpoint32f_s_
{
NXU_FLOAT f32X; /* X */
NXU_FLOAT f32Y; /* Y */
NXU_FLOAT f32Z; /* Z */
} NXU_3DPOINT32F_S;
/**
* @brief Multi-Points Definition.
*
* NXU_POLYGON_S: adjacent point order loop, first->two->three....->last->first,
* closed plane area, polygon shape.
* NXU_POLYLINE_S: adjacent point order, first->two->three....->last, open no loop
* NXU_POINTS_SET_S: adjacent point order, first, two, three, ...., last, points set
* NXU_FREEHAND_S: just points set, no order, no loop
*/
typedef struct _nxumultipoints_s_
{
NXU_S32 s32Num; /* Points Num */
NXU_POINT_S* pstPoints; /* Points set */
} NXU_POINTS_SET_S, NXU_POLYGON_S, NXU_POLYLINE_S, NXU_FREEHAND_S;
/* 2D Points Set with float data */
typedef struct _nxu2dpointsset32f_s_
{
NXU_S32 s32PtNum; /* Points Num */
NXU_POINT32F_S* pstPoints; /* Points Set */
} NXU_POINTS32F_SET_S;
/* Polygon, normally used in the config para */
#define NXU_MAX_VERTEX_NUM 16 /* max vertex num of the polygon */
typedef struct _nxupolygon_s_
{
NXU_U8 u8VertexNum; /* Points Num */
NXU_POINT_S astVertex[NXU_MAX_VERTEX_NUM]; /* Points Array */
} NXU_Polygon_S;
// 3D Points Set
typedef struct _nxu3dpointsset_s_
{
NXU_S32 s32PtNum; /* Points Num */
NXU_3DPOINT_S* pstPoints; /* Points Set */
} NXU_3DPOINTS_SET_S;
// 3D Points Set with float data
typedef struct _nxu3dpintsset32f_s_
{
NXU_S32 s32PtNum; /* Points Num */
NXU_3DPOINT32F_S* pstPoints; /* Points Set */
} NXU_3DPOINTS32F_SET_S;
/************************************************************************/
/* Line definition */
/************************************************************************/
/* Line */
/**
* @brief Multi-lines definition.
*
* maybe used for key-points set, or line-sets, or curve-sets, or ...
* bIsDirect: Defaults to NXU_FALSE.
* if true, the line is direct from start to end,
* otherwise, the line is not direct
*/
typedef struct _nxuline_s_
{
NXU_BOOL bIsDirect; /* Direct or Not */
NXU_POINT_S stStart; /* Start Point */
NXU_POINT_S stEnd; /* End Point */
} NXU_LINE_S;
/* Line with float data */
typedef struct _nxuline32f_s_
{
NXU_BOOL bIsDirect; /* Direct or Not */
NXU_POINT32F_S stStart; /* Start Point */
NXU_POINT32F_S stEnd; /* End Point */
} NXU_LINE32F_S;
/* Lines Set with default data */
typedef struct _nxu2dlineset_s_
{
NXU_S32 s32LineNum; /* Lines Num */
NXU_LINE_S* pstLines; /* Lines Set */
} NXU_LINES_SET_S;
/* Lines Set with float data */
typedef struct _nxu2Dlinesets32f_s_
{
NXU_S32 s32LineNum; /* Lines Num */
NXU_LINE32F_S* pstLineSet; /* Lines Set */
} NXU_2DLINES32F_SET_S;
/************************************************************************/
/* Shape Definition: Rect, Circle, Ellipse */
/************************************************************************/
/* Rect */
typedef struct _nxurect2p_s_
{
NXU_POINT_S stTopLeft; /* Top Left Point */
NXU_POINT_S stBottomRight; /* Bottom Right Point */
} NXU_RECT_S;
/* Rect use width and height */
typedef struct _nuxrectwh_s_
{
NXU_S32 s32X1; /* Top left x */
NXU_S32 s32Y1; /* Top left y */
NXU_S32 s32Width; /* Rect Width */
NXU_S32 s32Height; /* Rect Height */
}NXU_Rect_S;
/* Rect with float data */
typedef struct _nxurect32f2p_s_
{
NXU_POINT32F_S stTopLeft; /* Top Left Point */
NXU_POINT32F_S stBottomRight; /* Bottom Right Point */
} NXU_RECT32F_S;
/* Rect with float data use width and height */
typedef struct _nuxrect32fwh_s_
{
NXU_FLOAT f32X1; /* Top left x */
NXU_FLOAT f32Y1; /* Top left y */
NXU_FLOAT f32Width; /* Rect Width */
NXU_FLOAT f32Height; /* Rect Height */
} NXU_Rect32F_S;
/* Rotate Rect */
typedef struct _nxurotate_rect_s_
{
NXU_Rect32F_S stRect; /* Rectangle */
NXU_FLOAT f32Angle; /* Angle */
} NXU_ROTATE_Rect_S;
/* Circle */
typedef struct _nxucircle_s_
{
NXU_POINT_S stCenter; /* Center Point */
NXU_S32 s32Radius; /* Radius */
} NXU_CIRCLE_S;
/* Circle with float data */
typedef struct _nxucircle32f_s_
{
NXU_POINT32F_S stCenter; /* Center Point */
NXU_FLOAT f32Radius; /* Radius */
} NXU_CIRCLE32F_S;
/* Ellipse */
typedef struct _nxuellipse_s_
{
NXU_POINT_S stCenter; /* Center Point */
NXU_S32 s32RadiusX; /* Radius X */
NXU_S32 s32RadiusY; /* Radius Y */
} NXU_ELLIPSE_S;
/* Ellipse with float data */
typedef struct _nxuellipse32f_s_
{
NXU_POINT32F_S stCenter; /* Center Point */
NXU_FLOAT f32RadiusX; /* Radius X */
NXU_FLOAT f32RadiusY; /* Radius Y */
} NXU_ELLIPSE32F_S;
/************************************************************************/
/* STATUS_CODE */
/************************************************************************/
/**
* @brief all zero is ok, good status
* we can use the 'binary shift' and 'binary xor' or 'binary or' to get the status
*/
typedef enum _nxustatuscode_e_
{
NXU_SUCCESS = 0x00000000, /* Success */
NXU_FAILURE = 0x80000000, /* Failure */
/* memory failure */
NXU_MEM_FAIL = 0x80000001, /* memory failure */
NXU_MEM_FAIL_MALLOC = 0x80000002, /* memory allocation failure */
NXU_MEM_FAIL_CALLOC = 0x80000003, /* calloc */
NXU_MEM_FAIL_REALLOC = 0x80000004, /* realloc failure */
NXU_MEM_FAIL_FREE = 0x80000005, /* free memory failure */
/* image failure */
NXU_IMAGE_FAIL = 0x80000010, /* image failure */
NXU_IMAGE_FAIL_SIZE = 0x80000020, /* image size error */
NXU_IMAGE_FAIL_FORMAT = 0x80000030, /* image format error */
NXU_IMAGE_FAIL_READ = 0x80000040, /* read image file failure */
NXU_IMAGE_FAIL_WRITE = 0x80000050, /* write failure */
/* file failure */
NXU_FILE_FAIL = 0x80000100, /* file operation failure */
NXU_FILE_FAIL_READ = 0x80000200, /* read file failure */
NXU_FILE_FAIL_WRITE = 0x80000300, /* write file failure */
/* para failure */
NXU_PARA_FAIL = 0x80001000, /* parameter error */
NXU_PARA_FAIL_CONFIG = 0x80002000, /* config file failure */
NXU_PARA_FAIL_INVALID = 0x80003000, /* invalid parameter */
NXU_PARA_FAIL_UNSUPPORT = 0x80004000, /* not support */
/* license failure */
NXU_LICENSE_FAIL = 0x80010000, /* license failure */
NXU_LICENSE_FAIL_TIMEOUT = 0x80020000, /* license timeout */
NXU_LICENSE_FAIL_READ_MAC = 0x80030000, /* read mac failure */
NXU_LICENSE_FAIL_CHECK = 0x80040000, /* license check failure */
NXU_LICENSE_FAIL_INVALID = 0x80050000, /* license invalid */
/* net failure */
NXU_NETWORK_FAIL = 0x80100000, /* network failure */
NXU_NETWORK_CONNECT = 0x80200000, /* network connect failure */
NXU_NETWORK_PUSH = 0x80300000, /* network push failure */
NXU_NETWORK_GET = 0x80400000, /* network get failure */
} NXU_STATUS_CODE_E;
/**
* @brief if f32Real = 2, f32Imag = 3, then the complex number is 2+3i
* maybe used in Fourier
*/
typedef struct _nxucomplexdata_s_
{
NXU_FLOAT f32Real;
NXU_FLOAT f32Imag;
} NXU_Complex_S;
/************************************************************************/
/* Image and Matrix Definition */
/************************************************************************/
/**
* @brief the packed pixel, maybe rgb, yuv, hsv, gray, ...
*/
typedef struct _nxupackedpixel_s_
{
NXU_U8 u8ColorA;
NXU_U8 u8ColorB;
NXU_U8 u8ColorC;
NXU_U8 u8ColorReserved;
} NXU_PACKED_PIXEL_S;
/* BGR pixel value */
typedef struct _nxupixel_s_
{
NXU_U8 u8B; /* B */
NXU_U8 u8G; /* G */
NXU_U8 u8R; /* R */
} NXU_PIXEL_S, NXU_BGR_PIXEL_S;
/* HSV pixel value */
typedef struct _nxuhsvpixel_s_
{
NXU_FLOAT f32H; /* H */
NXU_FLOAT f32S; /* S */
NXU_FLOAT f32V; /* V */
} NXU_HSV_PIXEL_S;
/* HSL pixel value */
typedef struct _nxuhslpixel_s_
{
NXU_FLOAT f32H; /* H */
NXU_FLOAT f32S; /* S */
NXU_FLOAT f32L; /* L */
} NXU_HSL_PIXEL_S;
/**
* @brief Usual image format
* U(Cb), V(Cr)
*/
typedef enum _nxuimageformat_e_
{
NXU_IMAGE_FORMAT_UNKNOWN = 0, /* Unknown */
NXU_IMAGE_FORMAT_GRAY, /* GRAY GRAY GRAY GRAY */
NXU_IMAGE_FORMAT_RGB_PACKED, /* RGB RGB RGB RGB RGB RGB RGB RGB */
NXU_IMAGE_FORMAT_RGB_PLANAR, /* RRRRRRRR GGGGGGGG BBBBBBBBB */
NXU_IMAGE_FORMAT_BGR_PACKED, /* BGR BGR BGR BGR BGR BGR BGR BGR */
NXU_IMAGE_FORMAT_BGR_PLANAR, /* BBBBBBBBB GGGGGGGG RRRRRRRR */
NXU_IMAGE_FORMAT_HSV, /* hsv, but not often used */
NXU_IMAGE_FORMAT_HLS, /* hls, but not often used */
NXU_IMAGE_FORMAT_YUV444P, /* YYYYYYYY VVVVVVVV UUUUUUU */
NXU_IMAGE_FORMAT_YUV422P, /* YYYYYYYY VVVV UUUU */
NXU_IMAGE_FORMAT_YUV422_YUYV, /* YUYV YUYV YUYV YUYV */
NXU_IMAGE_FORMAT_YUV422_UYVY, /* UYVY UYVY UYVY UYVY */
NXU_IMAGE_FORMAT_YUV420p_YV12, /* YYYYYYYY VV UU */
NXU_IMAGE_FORMAT_YUV420p_I420, /* YYYYYYYY UU VV */
NXU_IMAGE_FORMAT_YUV420sp, /* YYYYYYYY UVUV, default */
NXU_IMAGE_FORMAT_YUV420_NV12, /* YYYYYYYY UVUV */
NXU_IMAGE_FORMAT_YUV420_NV21, /* YYYYYYYY VUVU */
NXU_IMAGE_FORMAT_YUV400, /* YYYYYYYY, only y */
NXU_IMAGE_FORMAT_BayerRGGB, /* RGGB RGGB RGGB RGGB RGGB RGGB */
NXU_IMAGE_FORMAT_BayerGRBG, /* GRBG GRBG GRBG GRBG GRBG GRBG */
NXU_IMAGE_FORMAT_BayerGBRG, /* GBRG GBRG GBRG GBRG GBRG GBRG */
NXU_IMAGE_FORMAT_BayerBGGR, /* BGGR BGGR BGGR BGGR BGGR BGGR */
NXU_IMAGE_FORMAT_BayerGR, /* GBRG GBRG GBRG GBRG GBRG GBRG */
NXU_IMAGE_FORMAT_BayerRG, /* RGGB RGGB RGGB RGGB RGGB RGGB */
NXU_IMAGE_FORMAT_BayerGB, /* GBRG GBRG GBRG GBRG GBRG GBRG */
NXU_IMAGE_FORMAT_BayerBG, /* BGGR BGGR BGGR BGGR BGGR BGGR */
NXU_IMAGE_FORMAT_UNSUPPORTED, /* unsupported format */
} NXU_IMAGE_FORMAT_E;
/**
* @brief
* DO REMEMBER: That the image is a matrix, not a Vector
* DO REMEMBER: FREE the memory after Use
* DO REMEMBER: How to store the data in the memory accord to the image format
*
*/
typedef struct _nxuimage_s_
{
NXU_S32 s32W; /* Width */
NXU_S32 s32H; /* Height */
NXU_U8* pData; /* Image Data */
NXU_IMAGE_FORMAT_E eFormat; /* Image Format */
} NXU_IMAGE_S;
/* 3-ch uchar Image Definition */
typedef struct _nxuimages3_s_
{
NXU_S32 s32W; /* Image width */
NXU_S32 s32H; /* Image height */
NXU_U8 *pu8D1; /* Channel 1 data pointer */
NXU_U8 *pu8D2; /* Channel 2 data pointer */
NXU_U8 *pu8D3; /* Channel 3 data pointer */
NXU_U32 u32Time; /* Time tag */
NXU_IMAGE_FORMAT_E enFormat; /* image format pointer */
NXU_U32 u32Flag; /* Reserved flag */
} NXU_IMAGE3_S;
/* Matrix with uchar data */
typedef struct _nxudatamat8u_s_
{
NXU_S32 s32Rows; /* Rows */
NXU_S32 s32Cols; /* Columns */
NXU_S32 s32Chns; /* Channels */
NXU_U8 *pu8Data; /* Data Buffer */
} NXU_MAT_8U_S;
/* Matrix with short data */
typedef struct _nxudatamat16s_s_
{
NXU_S32 s32Rows; /* Rows */
NXU_S32 s32Cols; /* Columns */
NXU_S32 s32Chns; /* Channels */
NXU_S16* ps16Data; /* Data Buffer */
} NXU_MAT_16S_S;
/* Matrix with int data */
typedef struct _nxudatamat32s_s_
{
NXU_S32 s32Rows; /* data rows */
NXU_S32 s32Cols; /* Columns */
NXU_S32 s32Chns; /* Channels */
NXU_S32* ps32Data; /* Data Buffer */
} NXU_MAT_32S_S;
/* Matrix with float data */
typedef struct _nxudatamat32f_s_
{
NXU_S32 s32Rows; /* data rows */
NXU_S32 s32Cols; /* Columns */
NXU_S32 s32Chns; /* Channels */
NXU_FLOAT* pf32Data; /* Data Buffer */
} NXU_MAT_32F_S;
/**
* @brief
* Do REMEMBER: How to store the data in the memory.
* if the mat store the image, do remember the data organization
* DO REMEMBER: Free the memory after use the matrix or image.
*/
typedef struct _nxumat_s_
{
NXU_S32 s32C; /* Channel */
NXU_S32 s32W; /* Width */
NXU_S32 s32H; /* Height */
NXU_VOID* pData; /* Data */
} NXU_EXIMAGE_S, NXU_MAT_S, NXU_MATRIX_S;
/* Image in RGB format */
typedef struct _nxurgbimage_s_
{
NXU_S32 s32W; /* Image width */
NXU_S32 s32H; /* Image height */
NXU_U8* pu8Data; /* Image data buffer */
NXU_U32 u32Time; /* Time tag */
} NXU_RGB_IMAGE_S;
/* Image in HSV format */
typedef struct _nxuhsvimage_s_
{
NXU_S32 s32W; /* Image width */
NXU_S32 s32H; /* Image height */
NXU_FLOAT *pf32Data; /* Image data buffer */
NXU_U32 u32Time; /* Time tag */
} NXU_HSV_IMAGE_S;
/* Gray image with uchar data */
typedef struct _nxugrayimage_s_
{
NXU_S32 s32W; /* Image width */
NXU_S32 s32H; /* Image height */
NXU_U8* pu8Data; /* Image data buffer */
} NXU_GRAY_IMAGE_S;
/* Gray image with short data, */
typedef struct _nxugrayimage16s_s_
{
NXU_S32 s32W; /* Image width */
NXU_S32 s32H; /* Image height */
NXU_S16* ps16Data; /* Image data buffer */
} NXU_GRAY_IMAGE16_S;
/* Gray Image with int data, maybe used in intergral image */
typedef struct _nxugrayimage32s_s_
{
NXU_S32 s32W; /* Image width */
NXU_S32 s32H; /* Image height */
NXU_S32* ps32Data; /* Image data buffer */
} NXU_GRAY_IMAGE32_S;
/************************************************************************/
/* Deep learning or Machine Learning object. */
/************************************************************************/
typedef struct _nxudlmltgt_s_
{
NXU_S32 s32LabelID; /* Label ID, name accord to project */
NXU_RECT_S stRect; /* Target Bounding Box */
NXU_FLOAT f32Prob; /* Probability */
} NXU_Target_S;
#ifdef __cplusplus
}
#endif /* end of __cplusplus*/
#endif /* end of file*/

View File

@ -0,0 +1,453 @@
/**
* @file nxu_vqd_api.h
* @brief VQD: Video Quality Diagnostic
*
* , , , , ,
*
* @author jwzhou (zhou24388@163.com)
* @version 1.0.0
* @date 2024-06-26
*
* @copyright Copyright (c) 2024 NXU Co,.Ltd.
*
* @par :
* Date : 2024-07-17
* version : 2.0.0
* Author : jwzhou
* Log :
* copyright: Copyright (c) 2024 NXU Co,.Ltd.
*
* @par :
* Date : 2024-06-29
* version : 1.0.1
* Author : jwzhou
* Log :
* copyright: Copyright (c) 2024 NXU Co,.Ltd.
*/
#ifndef __NXU_VQD_API_H__
#define __NXU_VQD_API_H__
#include "nxu_common.h"
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
/* ********************************************************************** */
/* .............................. Para Start ........................ */
/* ********************************************************************** */
/* 偏色参数
NEW: 20240717
*/
typedef struct __nxu_vqd_color_para_s
{
/* 偏色判断值, 大于该值即为偏色,
0.18, : 0~1, : 0.1~0.5,
, , , ,
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32ColorThr;
/* 偏色次数比例,
0.5, : 0~1, : 0.3~0.9 */
NXU_FLOAT f32ColorAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Color_Para_S;
/* 亮暗参数
NEW: 20240717
*/
typedef struct __nxu_vqd_lightdark_para_s
{
/* 超过200的像素比例,过亮阈值,
0.1, : 0~1, : 0.1~0.5,
, ,
, ,
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32LightThr;
/* 低于55的像素比例,过暗阈值,
0.4, : 0~1, : 0.2~0.6
, ,
, ,
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32DarkThr;
/* 偏暗或者偏亮次数比例, 连续多次检测出现的次数比例
0.5, : 0~1, : 0.3~0.9 */
NXU_FLOAT f32LgtDarkAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_LgtDark_Para_S;
/* 清晰度参数
NEW: 20240717
*/
typedef struct __nxu_vqd_clarity_para_s
{
/* 清晰度判断阈值, 小于该值判定为模糊,
0.4, : 0~1, : 0.3~0.99
, ,
,
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32ClarityThr;
/* 清晰度异常次数比例,
0.5, : 0~1, : 0.3~0.9 */
NXU_FLOAT f32ClarityAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Clarity_Para_S;
/* 噪声参数
NEW: 20240717
*/
typedef struct __nxu_vqd_noise_para_s
{
/* 噪声判断阈值,
0.3, : 0~1, : 0.2~0.8
, , , ,
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32NoiseThr;
/* 噪声次数比例,
0.6, : 0~1, : 0.3~0.9 */
NXU_FLOAT f32NoiseAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Noise_Para_S;
/* 遮挡参数 */
typedef struct __nxu_vqd_occlusion_para_s
{
/* 遮挡判断阈值, 大于该值判定为遮挡
0.1, : 0~1, : 0.05~0.5
, , , , */
NXU_FLOAT f32OcclusionThr;
/* 遮挡次数比例
0.5, : 0~1, : 0.3~0.9 */
NXU_FLOAT f32OcclusionAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Occlusion_Para_S;
/* 对比度参数
NEW: 20240717
*/
typedef struct __nxu_vqd_contrast_para_s
{
/* 低对比度判断阈值, 低于此值判定为低对比度,
0.2, : 0~1, : 0.1~0.3
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32CtraLowThr;
/* 高对比度判断阈值, 高于该值判定为高对比度,
0.8, : 0~1, : 0.7~0.9 */
NXU_FLOAT f32CtraHighThr;
/* 对比度异常次数比例,
0.5, : 0~1, : 0.3~0.9 */
NXU_FLOAT f32CtraAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Contrast_Para_S;
/* 蓝屏参数
NEW: 20240717
*/
typedef struct __nxu_vqd_blue_para_s
{
/* 蓝屏判断阈值, 蓝色像素占图像面积比例,
0.6, : 0~1, 0.4~0.9
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32BlueThr;
/* 蓝屏次数比例, 连续多帧中蓝色占比
0.5, : 0~1, : 0.3~0.9 */
NXU_FLOAT f32BlueAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Blue_Para_S;
/* 抖动参数, 做抖动检测, 尽量隔帧取帧做, */
typedef struct __nxu_vqd_shark_para_s
{
/* 抖动阈值参数
, , ,
0.2, : 0~10, : 0.1~0.8 */
NXU_FLOAT f32SharkThr;
/* 出现抖动次数的比例,
0.2, : 0~1, : 0.1~0.6 */
NXU_FLOAT f32SharkAbnNumRatio;
/* 全局运动幅度方差阈值, 理论上来讲, 抖动为全局画面移动, 方向幅度具有一致性, 方差应较小
0.2, : 0~1, : 0.05~0.5
, , , 使, */
NXU_FLOAT f32Resverd1;
/* 全局运动方向方差阈值, 小于该值时, 判定为全局运动, 可能会抖动
0.2, : 0~1, : 0.05~0.5
, , , 使, */
NXU_FLOAT f32Resverd2;
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Shark_Para_S;
/* 冻结参数 */
typedef struct __nxu_vqd_freeze_para_s
{
/* 冻结阈值参数
, 0.999, : 0.8~1, : 0.99~1
, , 1, */
NXU_FLOAT f32FreezeThr; /* 冻结阈值 */
/* 冻结帧数占得比例, 尽量设置的大一些, 避免那些无运动目标的静止监控画面
0.99, : 0.8~1, : 0.95~1, */
NXU_FLOAT f32FreezeAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Freeze_Para_S;
/* 马赛克检测
NEW: 20240717
*/
typedef struct __nxu_vqd_mosaic_para_s
{
/* 马赛克阈值参数
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32MosaicThr;
/* 马赛克次数比例, 连续多帧中马赛克占比
0.5,: 0~1, : 0.3
*/
NXU_FLOAT f32MosaicAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Mosaic_Para_S;
/* 花屏检测 NEW NEW NEW NEW NEW
NEW: 20240717
*/
typedef struct __nxu_vqd_flower_para_s
{
/* 花屏阈值参数
NEW: 20240717
0.1 : 0~1, : 0.1~0.9,
, ,
*/
NXU_FLOAT f32FlowerThr;
/* 花屏次数比例, 连续多帧中花屏占比
0.5, : 0~1, : 0.3
*/
NXU_FLOAT f32FlowerAbnNumRatio;
NXU_FLOAT f32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_FLOAT f32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Flower_Para_S;
typedef enum __nxu_vqd_func_enable_type_e
{
NXU_VQD_DISABLE_ALL = 0x00000000, /* 所有功能关闭 */
NXU_VQD_ENABLE_COLOR = 0x00000001, /* 偏色检测 */
NXU_VQD_ENABLE_LGTDARK = 0x00000002, /* 亮度检测 */
NXU_VQD_ENABLE_CLARITY = 0x00000004, /* 模糊检测 */
NXU_VQD_ENABLE_NOISE = 0x0000008, /* 噪声检测 */
NXU_VQD_ENABLE_CONTRAST = 0x00000010, /* 对比度检测 */
NXU_VQD_ENABLE_OCCLUSION = 0x00000020, /* 遮挡检测 */
NXU_VQD_ENABLE_BLUE = 0x00000040, /* 蓝屏检测 */
NXU_VQD_ENABLE_SHARK = 0x00000100, /* 抖动检测 */
NXU_VQD_ENABLE_FREEZE = 0x00000200, /* 冻结检测 */
NXU_VQD_ENABLE_MOSAIC = 0x00000400, /* 马赛克检测 NEW: 新增深度学习版本 20240717*/
NXU_VQD_ENABLE_FLOWER = 0x00000800, /* 花屏检测 NEW: 新增深度学习版本 20240717*/
NXU_VQD_ENABLE_ALL = 0x0FFFFFFF, /* 所有功能打开 */
} NXU_VQD_FuncEnable_Type_E;
/* 总参数 */
typedef struct __nxu_vqd_para_s
{
NXU_S32 s32VecFrmNum; /* 连续分析帧数(2-64), 默认为10, 最大为 64 */
NXU_S32 s32EnableFunc; /* 功能使能,默认为 NXU_VQD_ENABLE_ALL,多项功能用或 */
NXU_VQD_Color_Para_S stColorPara; /* 偏色参数 */
NXU_VQD_LgtDark_Para_S stLgtDarkPara; /* 亮暗参数 */
NXU_VQD_Clarity_Para_S stClarityPara; /* 清晰度参数 */
NXU_VQD_Noise_Para_S stNoisePara; /* 噪声参数 */
NXU_VQD_Contrast_Para_S stContrastPara; /* 对比度参数 */
NXU_VQD_Occlusion_Para_S stOcclusionPara; /* 遮挡参数 */
NXU_VQD_Blue_Para_S stBluePara; /* 蓝屏参数 */
NXU_VQD_Shark_Para_S stSharkPara; /* 抖动参数 */
NXU_VQD_Freeze_Para_S stFreezePara; /* 冻结参数 */
NXU_VQD_Mosaic_Para_S stMosaicPara; /* 马赛克参数 NEW: 新增深度学习版本 20240717*/
NXU_VQD_Flower_Para_S stFlowerPara; /* 花屏参数 NEW: 新增深度学习版本 20240717*/
NXU_BOOL bUseDeepLearning; /* 是否使用深度学习版本, 默认使用深度学习版本 NEW: 20240717*/
} NXU_VQD_Para_S;
/**
* ,
*/
typedef enum __nxu_vqd_abnormal_type_e
{
NXU_VQD_NORMAL = 0x00000000, /* 正常, 无异常 */
NXU_VQD_ABN_COLORDEV = 0x00000001, /* 偏色异常 */
NXU_VQD_ABN_LIGHT = 0x00000002, /* 过亮异常 */
NXU_VQD_ABN_DARK = 0x00000004, /* 过暗异常 */
NXU_VQD_ABN_CLARITY = 0x00000008, /* 清晰度异常 */
NXU_VQD_ABN_NOISE = 0x00000010, /* 噪声异常 */
NXU_VQD_ABN_CONTRAST = 0x00000020, /* 对比度异常 */
NXU_VQD_ABN_OCCLUSION = 0x00000040, /* 遮挡异常 */
NXU_VQD_ABN_BLUE = 0x00000080, /* 蓝屏异常 */
NXU_VQD_ABN_SHARK = 0x00000100, /* 抖动异常 */
NXU_VQD_ABN_FREEZE = 0x00000200, /* 冻结异常 */
NXU_VQD_ABN_MOSAIC = 0x00000400, /* 马赛克异常 NEW: 新增深度学习版本 20240717*/
NXU_VQD_ABN_FLOWER = 0x00000800, /* 花屏异常 NEW: 新增深度学习版本 20240717*/
NXU_VQD_ABN_ALL = 0x0FFFFFFF, /* 所有的异常 */
NXU_VQD_ABN_UNKNOWN = 0xF0000000, /* 序列不满时, 返回未知 */
} NXU_VQD_Abnormal_Type_E;
/* 异常结果
,, */
typedef struct __nxu_vqd_result_s
{
NXU_S32 s32AbnormalType; /* 异常类型,多种类型的,利用与或来确定异常类型 */
NXU_FLOAT f32ColorDev; /* 偏色检测 */
NXU_FLOAT f32LgtDark; /* 亮度检测 */
NXU_FLOAT f32Dark; /* 暗度检测 */
NXU_FLOAT f32Clarity; /* 清晰度检测 */
NXU_FLOAT f32Noise; /* 噪声检测 */
NXU_FLOAT f32Contrast; /* 对比度检测 */
NXU_FLOAT f32Blue; /* 蓝屏检测 */
NXU_FLOAT f32Occlusion; /* 遮挡检测 */
NXU_FLOAT f32Shark; /* 抖动检测 */
NXU_FLOAT f32Freeze; /* 冻结检测 */
NXU_FLOAT f32Mosaic; /* 马赛克检测 NEW: 新增深度学习版本 20240717*/
NXU_FLOAT f32Flower; /* 花屏检测 NEW: 新增深度学习版本 20240717*/
NXU_S32 s32Resverd1; /* 保留,暂时不设置,默认 -999 */
NXU_S32 s32Resverd2; /* 保留,暂时不设置,默认 -999 */
NXU_S32 s32Resverd3; /* 保留,暂时不设置,默认 -999 */
} NXU_VQD_Result_S;
/* ********************************************************************** */
/* .............................. API Start ........................ */
/* ********************************************************************** */
/**
* @brief ,,,使,
*
* @param phVQD
* @return NXU_EXPORTS
*/
NXU_EXPORTS NXU_STATUS_CODE_E NXU_VQD_Create(NXU_HANDLE *phVQD);
/**
* @brief ,使
*
* @param hVQD
* @return
*/
NXU_EXPORTS NXU_STATUS_CODE_E NXU_VQD_Destroy(NXU_HANDLE hVQD);
/**
* @brief
*
* @param pcLibVerStr
* @return
*/
NXU_EXPORTS NXU_STATUS_CODE_E NXU_VQD_GetLibVer(NXU_S8 *pcLibVerStr);
/**
* @brief
*
* @param hVQD
* @param pstPara
*
* @return
*/
NXU_EXPORTS NXU_STATUS_CODE_E
NXU_VQD_Config(NXU_HANDLE hVQD, const NXU_VQD_Para_S *pstPara);
/**
* @brief
*
* @param hVQD
* @param pstImage
* @return
*/
NXU_EXPORTS NXU_STATUS_CODE_E
NXU_VQD_Process(NXU_HANDLE hVQD, const NXU_IMAGE_S *pstImage);
/**
* @brief
*
* @param hVQD
* @param pstVQDResult
* @return
*/
NXU_EXPORTS NXU_STATUS_CODE_E
NXU_VQD_GetResults(NXU_HANDLE hVQD, NXU_VQD_Result_S *pstVQDResult);
/* ********************************************************************** */
/* .............................. API END.............................. */
/* ********************************************************************** */
#ifdef __cplusplus
}
#endif
#endif // !__NXU_VQD_API_H__

347
pkg/vqdcms/mode.go Normal file
View File

@ -0,0 +1,347 @@
package vqdcms
import (
"database/sql/driver"
"encoding/json"
"time"
)
type Abnormal struct {
Value float32 `json:"value"`
Name string `json:"name"`
}
type abnormal []Abnormal
func (r *abnormal) Scan(val interface{}) error {
b, _ := val.([]byte)
return json.Unmarshal(b, r)
}
func (r abnormal) Value() (driver.Value, error) {
return json.Marshal(r)
}
type DefaultValue struct {
Thr1 float32 `json:"thr1"`
Name1 string `json:"name1"`
Thr2 float32 `json:"thr2"`
Name2 string `json:"name2"`
Ratio float32 `json:"ratio"` //比例
}
type defaultValue []DefaultValue
func (r *defaultValue) Scan(val interface{}) error {
b, _ := val.([]byte)
return json.Unmarshal(b, r)
}
func (r defaultValue) Value() (driver.Value, error) {
return json.Marshal(r)
}
type AbnormalModel struct {
ID int `gorm:"primary_key" json:"id"`
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"`
FilePath string `json:"file_path"`
IsDeep bool `json:"is_deep"`
Abnormals abnormal `gorm:"type:json" json:"abnormals"`
DefaultValues defaultValue `gorm:"type:json" json:"default_values"` //设置的默认阈值
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
/* 偏色参数 */
type VQDColorPara struct {
ColorThr float32 /* 偏色判断值,*/
/* 偏色次数比例,
默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9 */
ColorAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/* 亮暗参数 */
// OK
type VQDLgtDarkPara struct {
/* 超过200的像素比例,过亮阈值,
默认 0.1, 取值范围: 0~1, 建议范围: 0.1~0.5,
值设置的越大, 越不灵敏, 就是非常亮才算过亮
值设置的越小, 越灵敏, 太小的话误报会很多 */
LightThr float32
/* 低于55的像素比例,过暗阈值,
默认 0.4, 取值范围: 0~1, 建议范围: 0.2~0.6
值设置的越大, 越不灵敏, 就是非常暗才算过暗
值设置的越小, 越灵敏, 太小的话误报会很多 */
DarkThr float32
/* 偏暗或者偏亮次数比例, 连续多次检测出现的次数比例
默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9 */
LgtDarkAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/* 清晰度参数 */
// OK
type VQDClarityPara struct {
/* 清晰度判断阈值, 小于该值判定为模糊,
默认为0.4, 取值范围: 0~1, 建议范围: 0.3~0.99
值设置的越大, 越不灵敏, 太大的话误报模糊会很多
值设置的越小, 就是非常模糊才算模糊 */
ClarityThr float32
/* 清晰度异常次数比例,
默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9 */
ClarityAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/* 噪声参数 */
type VQDNoisePara struct {
NoiseThr float32 /* 噪声判断阈值,默认为 35 */
/* 噪声次数比例,
默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9 */
NoiseAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/* 对比度参数 */
type VQDContrastPara struct {
CtraLowThr float32 /* 低对比度判断阈值,默认为 30 */
CtraHighThr float32 /* 高对比度判断阈值,默认为 120 */
/* 对比度异常次数比例,
默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9 */
CtraAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/* 遮挡参数 */
type VQDOcclusionPara struct {
OcclusionThr float32 /* 遮挡判断阈值,默认为 6 */
/* 遮挡次数比例
默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9 */
OcclusionAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/* 蓝屏参数 */
// OK
type VQDBluePara struct {
/* 蓝屏判断阈值, 蓝色像素占图像面积比例,
默认为 0.6, 取值范围: 0~1, 建议范围 0.4~0.9 */
BlueThr float32
/* 蓝屏次数比例, 连续多帧中蓝色占比
默认为0.5, 取值范围: 0~1, 建议范围: 0.1~0.9 */
BlueAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/* 抖动参数, 做抖动检测, 尽量隔帧取帧做, */
// OK
type VQDSharkPara struct {
/* 抖动阈值参数
全局运动均值, 无论是目标移动还是画面抖动, 全局运动均值相对来讲都会比较大,
默认为 0.2, 取值范围: 0~10, 建议范围: 0.1~0.8 */
SharkThr float32
/* 出现抖动次数的比例,
默认为0.2, 取值范围: 0~1, 建议范围: 0.1~0.6 */
SharkAbnNumRatio float32
/* 全局运动幅度方差阈值, 理论上来讲, 抖动为全局画面移动, 方向幅度具有一致性, 方差应较小
默认为0.2, 取值范围: 0~1, 建议范围: 0.05~0.5
如果内部有很多运动目标, 该值较大, 所以设置较大, 会使得在有运动目标时, 判定为抖动 */
Resverd1 float32
/* 全局运动方向方差阈值, 小于该值时, 判定为全局运动, 可能会抖动
默认为0.2, 取值范围: 0~1, 建议范围: 0.05~0.5
如果内部有很多运动目标, 该值较大, 所以设置较大, 会使得在有运动目标时, 判定为抖动 */
Resverd2 float32
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/* 冻结参数 */
// OK
type VQDFreezePara struct {
/* 冻结阈值参数
静止的像素比例, 默认为0.999, 取值范围: 0.8~1, 建议范围: 0.99~1
如果画面静止, 该值比较大, 甚至会接近于1, */
FreezeThr float32 /* 冻结阈值 */
/* 冻结帧数占得比例, 尽量设置的大一些, 避免那些无运动目标的静止监控画面
默认为0.99, 取值范围: 0.8~1, 建议范围: 0.95~1, */
FreezeAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/*
马赛克检测
NEW: 新增深度学习版本 2.0.0
*/
type VQDMosaicPara struct {
/* 马赛克阈值参数
NEW: 新增深度学习版本 20240717
默认为 0.1 取值范围: 0~1, 建议范围: 0.1~0.9,
大于该值判定为偏色, 小于该值判定为正常,
*/
MosaicThr float32
/* 马赛克次数比例, 连续多帧中马赛克占比
默认为0.5,取值范围: 0~1, 建议范围: 0.3
连续多帧中马赛克占比 */
MosaicAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
/*
花屏检测 NEW NEW NEW NEW NEW
NEW: 新增深度学习版本 2.0.0
*/
type VQDFlowerPara struct {
/* 花屏阈值参数
NEW: 新增深度学习版本 20240717
默认为 0.1 取值范围: 0~1, 建议范围: 0.1~0.9,
大于该值判定为偏色, 小于该值判定为正常,
*/
FlowerThr float32
/* 花屏次数比例, 连续多帧中花屏占比
默认为0.5, 取值范围: 0~1, 建议范围: 0.3
连续多帧中花屏占比 */
FlowerAbnNumRatio float32
Resverd1 float32 /* 保留,暂时不设置,默认 -999 */
Resverd2 float32 /* 保留,暂时不设置,默认 -999 */
Resverd3 float32 /* 保留,暂时不设置,默认 -999 */
}
type VQDPara struct {
VecFrmNum int /* 连续分析帧数(2-64), 默认为10, 最大为 64 */
UseDeepLearning bool /* 是否使用深度学习版本, 默认使用深度学习版本 NEW: 20240717 */
EnableFunc int /* 功能使能,默认为 NXU_VQD_ENABLE_ALL,多项功能用或 */
ColorPara VQDColorPara /* 偏色参数 */
LgtDarkPara VQDLgtDarkPara /* 亮暗参数 */
ClarityPara VQDClarityPara /* 清晰度参数 */
NoisePara VQDNoisePara /* 噪声参数 */
ContrastPara VQDContrastPara /* 对比度参数 */
OcclusionPara VQDOcclusionPara /* 遮挡参数 */
BluePara VQDBluePara /* 蓝屏参数 */
SharkPara VQDSharkPara /* 抖动参数 */
FreezePara VQDFreezePara /* 冻结参数 */
MosaicPara VQDMosaicPara /* 马赛克检测 */
FlowerPara VQDFlowerPara /* 花屏检测 */
}
// config cvrdo.VQDAlgoConfig
func NewVQDPara() VQDPara {
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"}),
ColorPara: VQDColorPara{
ColorThr: 0.3,
ColorAbnNumRatio: 0.6,
},
LgtDarkPara: VQDLgtDarkPara{
LightThr: 0.3,
DarkThr: 0.4,
LgtDarkAbnNumRatio: 0.6,
},
ClarityPara: VQDClarityPara{
ClarityThr: 0.5,
ClarityAbnNumRatio: 0.6,
},
NoisePara: VQDNoisePara{
NoiseThr: 0.3,
NoiseAbnNumRatio: 0.6,
},
ContrastPara: VQDContrastPara{
CtraLowThr: 0.3,
CtraHighThr: 0.8,
CtraAbnNumRatio: 0.6,
},
OcclusionPara: VQDOcclusionPara{
OcclusionThr: 0.1,
OcclusionAbnNumRatio: 0.6,
},
BluePara: VQDBluePara{
BlueThr: 0.3,
BlueAbnNumRatio: 0.6,
},
SharkPara: VQDSharkPara{
SharkThr: 0.2,
SharkAbnNumRatio: 0.2,
},
FreezePara: VQDFreezePara{
FreezeThr: 0.999,
FreezeAbnNumRatio: 0.99,
},
MosaicPara: VQDMosaicPara{
MosaicThr: 0.3,
MosaicAbnNumRatio: 0.6,
},
FlowerPara: VQDFlowerPara{
FlowerThr: 0.3,
FlowerAbnNumRatio: 0.6,
},
}
}
/*
异常结果
当前各异常返回出来的值,作为参考,外面也可以存到列表中进行应用
*/
type VQDResult struct {
AbnormalType int /* 异常类型,多种类型的,利用与或来确定异常类型 */
ColorDev float32 /* 偏色检测 */
LgtDark float32 /* 亮暗检测 */
Clarity float32 /* 清晰度检测 */
Noise float32 /* 噪声检测 */
Contrast float32 /* 对比度检测 */
Occlusion float32 /* 遮挡检测 */
Blue float32 /* 蓝屏检测 */
Shark float32 /* 抖动检测 */
Freeze float32 /* 冻结检测 */
Mosaic float32 /* 马赛克检测 NEW: 新增深度学习版本 2.0.0*/
Flower float32 /* 花屏检测 NEW: 新增深度学习版本 2.0.0*/
Resverd1 int /* 保留,暂时不设置,默认 -999 */
Resverd2 int /* 保留,暂时不设置,默认 -999 */
Resverd3 int /* 保留,暂时不设置,默认 -999 */
}

92
pkg/vqdcms/type.go Normal file
View File

@ -0,0 +1,92 @@
package vqdcms
const NXU_VQD_DISABLE_ALL = 0x00000000 /* 所有功能关闭 */
const NXU_VQD_ENABLE_COLOR = 0x00000001 /* 偏色检测 */
const NXU_VQD_ENABLE_LGTDARK = 0x00000002 /* 过亮过暗检测 */
const NXU_VQD_ENABLE_CLARITY = 0x00000004 /* 模糊检测 */
const NXU_VQD_ENABLE_NOISE = 0x0000008 /* 噪声检测 */
const NXU_VQD_ENABLE_CONTRAST = 0x00000010 /* 对比度检测 */
const NXU_VQD_ENABLE_OCCLUSION = 0x00000020 /* 遮挡检测 */
const NXU_VQD_ENABLE_BLUE = 0x00000040 /* 蓝屏检测 */
const NXU_VQD_ENABLE_SHARK = 0x00000100 /* 抖动检测 */
const NXU_VQD_ENABLE_FREEZE = 0x00000200 /* 冻结检测 */
const NXU_VQD_ENABLE_MOSAIC = 0x00000400 /* 马赛克检测 NEW: 新增深度学习版本 20240717*/
const NXU_VQD_ENABLE_FLOWER = 0x00000800 /* 花屏检测 NEW: 新增深度学习版本 20240717*/
const NXU_VQD_ENABLE_ALL = 0x0FFFFFFF /* 所有功能打开 */
var TypeMap = map[string]int{
"vqd_color": NXU_VQD_ENABLE_COLOR,
"vqd_lgt_dark": NXU_VQD_ENABLE_LGTDARK,
"vqd_clarity": NXU_VQD_ENABLE_CLARITY,
"vqd_noise": NXU_VQD_ENABLE_NOISE,
"vqd_contrast": NXU_VQD_ENABLE_CONTRAST,
"vqd_occlusion": NXU_VQD_ENABLE_OCCLUSION,
"vqd_blue": NXU_VQD_ENABLE_BLUE,
"vqd_shark": NXU_VQD_ENABLE_SHARK,
"vqd_freeze": NXU_VQD_ENABLE_FREEZE,
"vqd_mosaic": NXU_VQD_ENABLE_MOSAIC,
"vqd_flower": NXU_VQD_ENABLE_FLOWER,
}
func GetAlgoEnable(str []string) int {
v := NXU_VQD_DISABLE_ALL
if len(str) <= 0 {
return v
}
for i := 0; i < len(str); i++ {
if vqd, ok := TypeMap[str[i]]; ok {
v |= vqd
}
}
return v
}
/**
* 如果返回的是多个异常类型, 则可以用与或来确定具体异常类型
*/
const NXU_VQD_NORMAL = 0x00000000 /* 正常, 无异常 */
const NXU_VQD_ABN_COLORDEV = 0x00000001 /* 偏色异常 */
const NXU_VQD_ABN_LIGHT = 0x00000002 /* 过亮异常 */
const NXU_VQD_ABN_DARK = 0x00000004 /* 过暗异常 */
const NXU_VQD_ABN_CLARITY = 0x00000008 /* 清晰度异常 */
const NXU_VQD_ABN_NOISE = 0x00000010 /* 噪声异常 */
const NXU_VQD_ABN_CONTRAST = 0x00000020 /* 对比度异常 */
const NXU_VQD_ABN_OCCLUSION = 0x00000040 /* 遮挡异常 */
const NXU_VQD_ABN_BLUE = 0x00000080 /* 蓝屏异常 */
const NXU_VQD_ABN_SHARK = 0x00000100 /* 抖动异常 */
const NXU_VQD_ABN_FREEZE = 0x00000200 /* 冻结异常 */
const NXU_VQD_ABN_MOSAIC = 0x00000400 /* 马赛克异常 NEW: 新增深度学习版本 20240717*/
const NXU_VQD_ABN_FLOWER = 0x00000800 /* 花屏异常 NEW: 新增深度学习版本 20240717*/
const NXU_VQD_ABN_ALL = 0x0FFFFFFF /* 所有的异常 */
const NXU_VQD_ABN_UNKNOWN = 0xF0000000 /* 序列不满时, 返回位置 */
var ALNORMAL_NAMES = map[int]string{
NXU_VQD_NORMAL: "无异常",
NXU_VQD_ABN_COLORDEV: "偏色异常",
NXU_VQD_ABN_LIGHT: "过亮异常",
NXU_VQD_ABN_DARK: "过暗异常",
NXU_VQD_ABN_CLARITY: "清晰度异常",
NXU_VQD_ABN_NOISE: "噪声异常",
NXU_VQD_ABN_CONTRAST: "对比度异常",
NXU_VQD_ABN_OCCLUSION: "遮挡异常",
NXU_VQD_ABN_BLUE: "蓝屏异常",
NXU_VQD_ABN_SHARK: "抖动异常",
NXU_VQD_ABN_FREEZE: "冻结异常",
NXU_VQD_ABN_MOSAIC: "马赛克异常",
NXU_VQD_ABN_FLOWER: "花屏异常",
NXU_VQD_ABN_ALL: "所有的异常",
NXU_VQD_ABN_UNKNOWN: "序列不满",
}
type Children struct {
Name string `json:"name"`
Type string `json:"type"`
Placeholder string `json:"placeholder"`
Value interface{} `json:"value"`
}
type ArithmeticType struct {
Name string `json:"name"`
Type string `json:"type"`
Children []Children `json:"children"`
}

437
pkg/vqdcms/vqd.go Normal file
View File

@ -0,0 +1,437 @@
package vqdcms
/*
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
*/
import "C"
import (
"easyvqd/pkg/decoder"
"fmt"
"github.com/shirou/gopsutil/v4/mem"
"log/slog"
"os"
"path/filepath"
"runtime/debug"
"strings"
"sync"
"sync/atomic"
"time"
"unsafe"
)
const MAX_STREAM_CHAN_NUM = 256
type VideoInfoVQD struct {
mu sync.RWMutex
VQDHandle uintptr
Params VQDPara
IsCreateSuccess bool
}
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
}
type VQDResultCB func(AbnormalModel)
type ChanData struct {
data []byte
w int
h int
now time.Time
}
func IsCurTimeInRecordPlan(recordPlanNew string, now time.Time) bool {
return false
}
func NewVQDHandle(cb VQDResultCB, taskId int, chnId string) *VQDHandle {
v := &VQDHandle{
running: 0,
decoder: &decoder.VideoDecoder{},
ChnID: chnId,
TaskID: taskId,
data: make(chan ChanData, MAX_STREAM_CHAN_NUM),
cb: cb,
handle: &VideoInfoVQD{},
}
err := v.decoder.Create()
if err != nil {
slog.Error("decoder Create ", "taskId", chnId, "err", err)
}
return v
}
func (v *VQDHandle) SetVQDConfig(params VQDPara) error {
return v.handle.Config(params, params.EnableFunc, 10)
}
func (v *VQDHandle) GetHandle() *VideoInfoVQD {
return v.handle
}
func (v *VQDHandle) Create(params VQDPara) *VQDHandle {
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())
return v
}
atomic.StoreUint32(&v.running, 1)
go v.RunFrame()
return v
}
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)
}
v.data = nil
if atomic.LoadUint32(&v.running) == 1 {
atomic.StoreUint32(&v.running, 0)
v.handle.Destroy()
}
}
func (v *VQDHandle) RunFrame() {
defer func() {
if e := recover(); e != nil {
print(fmt.Sprintf("RunFrame---%s\n", e))
print(fmt.Sprintf("%s\n", string(debug.Stack())))
}
}()
cvqdImgsDir := filepath.Join(CWD(), "vqd_images", fmt.Sprintf("%d", v.TaskID))
cvqdImgsDir = filepath.ToSlash(cvqdImgsDir)
if err := os.MkdirAll(cvqdImgsDir, os.ModePerm); err != nil {
//llog.Info("%s:%s", cvqdImgsDir, err.Error())
}
hyper := 0
index := 0
cdata := ChanData{}
for {
select {
case data, ok := <-v.data:
if !ok {
return
}
if len(v.data) >= (MAX_STREAM_CHAN_NUM - 6) {
//llog.Info("vqd channel num >= %d", MAX_STREAM_CHAN_NUM)
hyper = MAX_STREAM_CHAN_NUM / 10
}
if hyper > 0 {
hyper--
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()
}
now := time.Now().UnixMilli()
fpath := filepath.Join(cvqdImgsDir, fmt.Sprintf("%d_%s_%d.jpg", v.TaskID, v.ChnID, now))
fpath = filepath.ToSlash(fpath)
result := VQDResult{}
ret := v.handle.Frame(cdata.data, cdata.w, cdata.h, index, fpath, &result)
if ret == 0 {
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
if v.cb != nil {
v.cb(value)
} else {
// 保存数据库
//CreateAbnormalModel(&value)
}
}
}
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++
}
}
}
func (v *VQDHandle) parseVQD(result VQDResult) (AbnormalModel, bool) {
isabnormal := false
abnormals := AbnormalModel{
IsDeep: v.handle.Params.UseDeepLearning,
Abnormals: make([]Abnormal, 0),
}
if (result.AbnormalType & NXU_VQD_ABN_COLORDEV) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.ColorDev,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_COLORDEV],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.ColorPara.ColorThr,
Name1: "偏色阈值",
Ratio: v.handle.Params.ColorPara.ColorAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_LIGHT) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.LgtDark,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_LIGHT],
})
}
if (result.AbnormalType & NXU_VQD_ABN_DARK) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.LgtDark,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_DARK],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.LgtDarkPara.LightThr,
Name1: "过亮阈值",
Thr2: v.handle.Params.LgtDarkPara.DarkThr,
Name2: "过暗阈值",
Ratio: v.handle.Params.LgtDarkPara.LgtDarkAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_CLARITY) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Clarity,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_CLARITY],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.ClarityPara.ClarityThr,
Name1: "清晰度阈值",
Ratio: v.handle.Params.ClarityPara.ClarityAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_NOISE) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Noise,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_NOISE],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.NoisePara.NoiseThr,
Name1: "噪声阈值",
Ratio: v.handle.Params.NoisePara.NoiseAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_CONTRAST) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Contrast,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_CONTRAST],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.ContrastPara.CtraLowThr,
Name1: "低对比度阈值",
Thr2: v.handle.Params.ContrastPara.CtraHighThr,
Name2: "高对比度阈值",
Ratio: v.handle.Params.ContrastPara.CtraAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_OCCLUSION) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Occlusion,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_OCCLUSION],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.OcclusionPara.OcclusionThr,
Name1: "遮挡阈值",
Ratio: v.handle.Params.OcclusionPara.OcclusionAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_BLUE) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Blue,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_BLUE],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.BluePara.BlueThr,
Name1: "蓝屏阈值",
Ratio: v.handle.Params.BluePara.BlueAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_SHARK) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Shark,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_SHARK],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.SharkPara.SharkThr,
Name1: "抖动阈值",
Ratio: v.handle.Params.SharkPara.SharkAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_FREEZE) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Freeze,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_FREEZE],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.FreezePara.FreezeThr,
Name1: "冻结阈值",
Ratio: v.handle.Params.FreezePara.FreezeAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_MOSAIC) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Mosaic,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_MOSAIC],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.MosaicPara.MosaicThr,
Name1: "马赛克阈值",
Ratio: v.handle.Params.MosaicPara.MosaicAbnNumRatio,
})
if (result.AbnormalType & NXU_VQD_ABN_FLOWER) != 0 {
isabnormal = true
abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
Value: result.Flower,
Name: ALNORMAL_NAMES[NXU_VQD_ABN_FLOWER],
})
}
abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
Thr1: v.handle.Params.FlowerPara.FlowerThr,
Name1: "花屏阈值",
Ratio: v.handle.Params.FlowerPara.FlowerAbnNumRatio,
})
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)
if err != nil {
slog.Error("I帧转YUV失败: ", "TaskID", v.TaskID, "err", err)
return
}
if len(data) > 0 && v.data != nil {
dst := make([]byte, len(data))
copy(dst, data)
now := time.Now()
d := ChanData{
data: dst,
w: w,
h: h,
now: now,
}
//v.dataLock.Lock()
//v.data = append(v.data, d)
//v.dataLock.Unlock()
v.data <- d
}
}
// IFrameData 封装I帧数据和指针信息
type IFrameData struct {
Data []byte // I帧原始字节数据
Pointer unsafe.Pointer // 指向数据的原始指针
Length int // 数据长度(字节数)
IsValid bool // 指针是否有效
}
// GetIFramePointer 将字节切片转换为原始指针
// 注意unsafe包的使用会绕过Go的内存安全检查需谨慎
func GetIFramePointer(data []byte) *IFrameData {
if len(data) == 0 {
return &IFrameData{
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 &IFrameData{
Data: data,
Pointer: ptr,
Length: len(data),
IsValid: true,
}
}
func CWD() string {
geTwd, err := os.Getwd()
if err != nil {
return ""
}
return geTwd
}

314
pkg/vqdcms/vqd_linux.go Normal file
View File

@ -0,0 +1,314 @@
package vqdcms
/*
#cgo linux CFLAGS: -w -I${SRCDIR}/include -fPIC
#cgo linux CPPFLAGS: -w -I${SRCDIR}/include -fPIC
#cgo linux LDFLAGS: -Wl,-rpath,'$ORIGIN/./VqdSDK' -rdynamic -ldl
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include "nxu_common.h"
#include "nxu_vqd_api.h"
#include "ez_vqd.h"
#define LIB_NAME_VQD "libEasyVQD.so"
void *g_hDll = NULL;
typedef int (*vqdLoad)(char *, int , char*);
typedef int (*vqdUnLoad)();
typedef int (*vqdCreate)(VideoInfoCVR*);
typedef int (*vqdDestroy)(VideoInfoCVR*);
typedef int (*vqdConfig)(VideoInfoCVR, NXU_VQD_Para_S *, int);
typedef int (*vqdRun)(VideoInfoCVR, int, int, char*, int, char*, int, NXU_VQD_Result_S*);
static vqdLoad _vqdLoad = NULL;
static vqdUnLoad _vqdUnLoad = NULL;
static vqdCreate _vqdCreate = NULL;
static vqdDestroy _vqdDestroy = NULL;
static vqdConfig _vqdConfig = NULL;
static vqdRun _vqdRun = NULL;
int VQDCreate(VideoInfoCVR* handle)
{
if (_vqdCreate)
{
return _vqdCreate(handle);
}
return -99;
}
int VQDDestroy(VideoInfoCVR* handle)
{
if (_vqdDestroy)
{
return _vqdDestroy(handle);
}
return -99;
}
int VQDConfig(VideoInfoCVR handle, NXU_VQD_Para_S *stPara, int interval)
{
if (_vqdConfig)
{
return _vqdConfig(handle, stPara, interval);
}
return -99;
}
int VQDRun(VideoInfoCVR handle, int width, int height, char* data, int frameNum, char* save_filename, int save_filename_size, NXU_VQD_Result_S* out_stResult)
{
if (_vqdRun)
{
return _vqdRun(handle, width, height, data, frameNum, save_filename, save_filename_size, out_stResult);
}
return -99;
}
void lib_error(char *msg)
{
const char* err = dlerror();
sprintf(msg, "load error[%s]:%s\n", LIB_NAME_VQD, err);
}
int VQDLoad(char *path, int path_size, char *errmsg)
{
if (g_hDll)
{
return 0;
}
char sopath[512] = {0};
memset(sopath, 0x00, 512);
sprintf(sopath, "%s/%s", path, LIB_NAME_VQD);
g_hDll = dlopen(sopath, RTLD_NOW | RTLD_GLOBAL);
if (g_hDll)
{
int ret = 0;
do
{
_vqdLoad = dlsym(g_hDll, "ez_cvr_vqd_load");
if (_vqdLoad == NULL)
{
ret = -3;
break;
}
_vqdUnLoad = dlsym(g_hDll, "ez_cvr_vqd_unload");
_vqdCreate = dlsym(g_hDll, "ez_cvr_vqd_create");
_vqdDestroy = dlsym(g_hDll, "ez_cvr_vqd_destroy");
_vqdConfig = dlsym(g_hDll, "ez_cvr_vqd_config");
_vqdRun = dlsym(g_hDll, "ez_cvr_vqd_run");
ret = _vqdLoad(path, path_size, errmsg);
if (ret != 0)
{
return -5;
}
}while(0);
if (ret != 0)
{
lib_error(errmsg);
}
return ret;
}
lib_error(errmsg);
return -2;
}
int VQDUnLoad()
{
if (_vqdUnLoad)
{
_vqdUnLoad();
}
_vqdUnLoad = NULL;
if (g_hDll)
{
dlclose(g_hDll);
}
g_hDll = NULL;
return 0;
}
*/
import "C"
import (
"fmt"
"log/slog"
"path/filepath"
"unsafe"
)
var (
vqdInitDLL = false
)
func VQDInit() (err error) {
if vqdInitDLL {
return
}
cwd, _ := os.Getwd()
vqdSDKDir := filepath.Join(cwd, "VqdSDK")
var errmsg [128]C.char
ret := C.VQDLoad(C.CString(vqdSDKDir), C.int(len(vqdSDKDir)), &errmsg[0])
if ret != 0 {
VQDUnInit()
resBuf1 := (*[128]byte)(unsafe.Pointer(&errmsg[0]))[:128:128]
slog.Info("vqd cms open", "ret", int(ret), "err", string(resBuf1))
return fmt.Errorf("[%d]:%s", int(ret), resBuf1)
}
vqdInitDLL = true
slog.Info("vqd cms open ok")
return
}
func VQDUnInit() {
C.VQDUnLoad()
vqdInitDLL = false
slog.Info("vqd cms stop ok")
}
func (v *VideoInfoVQD) Create(params VQDPara, enable int, interval int) error {
v.mu.Lock()
defer v.mu.Unlock()
v.Params = params
var stPara C.NXU_VQD_Para_S
setParams(&stPara, params, enable)
var handle C.VideoInfoCVR
ret := int(C.VQDCreate(&handle))
if ret != 0 {
return fmt.Errorf("ez_cvr_vqd_create fail:%d", ret)
}
v.VQDHandle = uintptr(handle)
C.VQDConfig((C.VideoInfoCVR)(unsafe.Pointer(v.VQDHandle)), &stPara, C.int(interval))
v.IsCreateSuccess = true
return nil
}
func (v *VideoInfoVQD) Config(params VQDPara, enable int, interval int) error {
v.mu.Lock()
defer v.mu.Unlock()
if !v.IsCreateSuccess {
return fmt.Errorf("vqd cms is not create")
}
v.Params = params
var stPara C.NXU_VQD_Para_S
setParams(&stPara, params, enable)
ret := int(C.VQDConfig((C.VideoInfoCVR)(unsafe.Pointer(v.VQDHandle)), &stPara, C.int(interval)))
if ret != 0 {
return fmt.Errorf("ez_cvr_vqd_config fail:%d", ret)
}
return nil
}
func setParams(stPara *C.NXU_VQD_Para_S, params VQDPara, enable int) {
if params.UseDeepLearning {
stPara.bUseDeepLearning = C.NXU_TRUE
} else {
stPara.bUseDeepLearning = C.NXU_FALSE
}
//enable := NXU_VQD_ENABLE_LGTDARK
// 设置参数
// 设置亮度检测
stPara.s32EnableFunc = C.NXU_S32(enable)
stPara.stLgtDarkPara.f32DarkThr = C.NXU_FLOAT(params.LgtDarkPara.DarkThr)
stPara.stLgtDarkPara.f32LightThr = C.NXU_FLOAT(params.LgtDarkPara.LightThr)
stPara.stLgtDarkPara.f32LgtDarkAbnNumRatio = C.NXU_FLOAT(params.LgtDarkPara.LgtDarkAbnNumRatio)
stPara.s32VecFrmNum = C.int(params.VecFrmNum)
//enable |= NXU_VQD_ENABLE_BLUE
// 设置蓝屏检测
stPara.stBluePara.f32BlueThr = C.NXU_FLOAT(params.BluePara.BlueThr)
stPara.stBluePara.f32BlueAbnNumRatio = C.NXU_FLOAT(params.BluePara.BlueAbnNumRatio)
//enable |= NXU_VQD_ENABLE_CLARITY
// 设置清晰度检测
stPara.stClarityPara.f32ClarityThr = C.NXU_FLOAT(params.ClarityPara.ClarityThr)
stPara.stClarityPara.f32ClarityAbnNumRatio = C.NXU_FLOAT(params.ClarityPara.ClarityAbnNumRatio)
//enable |= NXU_VQD_ENABLE_SHARK
// 设置抖动检测
stPara.stSharkPara.f32SharkThr = C.NXU_FLOAT(params.SharkPara.SharkThr)
stPara.stSharkPara.f32SharkAbnNumRatio = C.NXU_FLOAT(params.SharkPara.SharkAbnNumRatio)
//enable |= NXU_VQD_ENABLE_FREEZE
// 设置冻结检测
stPara.stFreezePara.f32FreezeThr = C.NXU_FLOAT(params.FreezePara.FreezeThr)
stPara.stFreezePara.f32FreezeAbnNumRatio = C.NXU_FLOAT(params.FreezePara.FreezeAbnNumRatio)
//enable |= NXU_VQD_ENABLE_COLOR
// 设置偏色检测
stPara.stColorPara.f32ColorThr = C.NXU_FLOAT(params.ColorPara.ColorThr)
stPara.stColorPara.f32ColorAbnNumRatio = C.NXU_FLOAT(params.ColorPara.ColorAbnNumRatio)
//enable |= NXU_VQD_ENABLE_OCCLUSION
// 设置遮挡检测
stPara.stOcclusionPara.f32OcclusionThr = C.NXU_FLOAT(params.OcclusionPara.OcclusionThr)
stPara.stOcclusionPara.f32OcclusionAbnNumRatio = C.NXU_FLOAT(params.OcclusionPara.OcclusionAbnNumRatio)
//enable |= NXU_VQD_ENABLE_NOISE
// 设置噪声检测参数
stPara.stNoisePara.f32NoiseThr = C.NXU_FLOAT(params.NoisePara.NoiseThr)
stPara.stNoisePara.f32NoiseAbnNumRatio = C.NXU_FLOAT(params.NoisePara.NoiseAbnNumRatio)
//enable |= NXU_VQD_ENABLE_CONTRAST
// 设置对比度检测
stPara.stContrastPara.f32CtraLowThr = C.NXU_FLOAT(params.ContrastPara.CtraLowThr)
stPara.stContrastPara.f32CtraHighThr = C.NXU_FLOAT(params.ContrastPara.CtraHighThr)
stPara.stContrastPara.f32CtraAbnNumRatio = C.NXU_FLOAT(params.ContrastPara.CtraAbnNumRatio)
//enable |= NXU_VQD_ENABLE_MOSAIC
// 设置马赛克检测
stPara.stMosaicPara.f32MosaicThr = C.NXU_FLOAT(params.MosaicPara.MosaicThr)
stPara.stMosaicPara.f32MosaicAbnNumRatio = C.NXU_FLOAT(params.MosaicPara.MosaicAbnNumRatio)
//enable |= NXU_VQD_ENABLE_FLOWER
// 设置花屏检测
stPara.stFlowerPara.f32FlowerThr = C.NXU_FLOAT(params.FlowerPara.FlowerThr)
stPara.stFlowerPara.f32FlowerAbnNumRatio = C.NXU_FLOAT(params.FlowerPara.FlowerAbnNumRatio)
}
func (v *VideoInfoVQD) Frame(data []byte, w, h, frameNum int, save_filename string, outResult *VQDResult) int {
v.mu.Lock()
defer v.mu.Unlock()
if !v.IsCreateSuccess {
slog.Info("vqd cms is not create")
return -10
}
result := &C.NXU_VQD_Result_S{}
yuv := C.CBytes(data)
defer C.free(yuv)
filename := C.CString(save_filename)
defer C.free(unsafe.Pointer(filename))
ret := int(C.VQDRun((C.VideoInfoCVR)(unsafe.Pointer(v.VQDHandle)), C.int(w), C.int(h), (*C.char)(yuv),
C.int(frameNum), filename, C.int(len(save_filename)), result))
if ret == 0 {
// 分析成功
outResult.AbnormalType = int(result.s32AbnormalType)
outResult.ColorDev = float32(result.f32ColorDev)
outResult.LgtDark = float32(result.f32LgtDark)
outResult.Clarity = float32(result.f32Clarity)
outResult.Noise = float32(result.f32Noise)
outResult.Contrast = float32(result.f32Contrast)
outResult.Occlusion = float32(result.f32Occlusion)
outResult.Blue = float32(result.f32Blue)
outResult.Shark = float32(result.f32Shark)
outResult.Freeze = float32(result.f32Freeze)
outResult.Mosaic = float32(result.f32Mosaic)
outResult.Flower = float32(result.f32Flower)
}
return ret
}
func (v *VideoInfoVQD) Destroy() {
v.mu.Lock()
defer v.mu.Unlock()
if !v.IsCreateSuccess {
slog.Info("vqd cms Destroy fail")
return
}
h := (C.VideoInfoCVR)(unsafe.Pointer(v.VQDHandle))
v.IsCreateSuccess = false
C.VQDDestroy(&h)
}

256
pkg/vqdcms/vqd_windows.go Normal file
View File

@ -0,0 +1,256 @@
package vqdcms
/*
#cgo CFLAGS: -w -I${SRCDIR}/include -fPIC
#cgo CPPFLAGS: -w -I${SRCDIR}/include -fPIC
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "nxu_common.h"
#include "nxu_vqd_api.h"
#include "ez_vqd.h"
#include <windows.h>
void LoadDllDir(char *path, int path_size)
{
SetDllDirectoryA(path);
}
*/
import "C"
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"syscall"
"unsafe"
)
var (
vqdDLL *syscall.DLL
vqdLoad *syscall.Proc
vqdUnLoad *syscall.Proc
vqdCreate, vqdDestroy, vqdConfig, vqdRun *syscall.Proc
)
func VQDInit() (err error) {
if vqdDLL != nil {
return
}
cwd, _ := os.Getwd()
vqdSDKDir := filepath.Join(cwd, "VqdSDK")
C.LoadDllDir(C.CString(vqdSDKDir), C.int(len(vqdSDKDir)))
vqdDLL, err = syscall.LoadDLL(filepath.Join(vqdSDKDir, "EasyVQD.dll"))
if err != nil {
return
}
vqdLoad, err = vqdDLL.FindProc("ez_cvr_vqd_load")
if err != nil {
VQDUnInit()
return
}
vqdUnLoad, err = vqdDLL.FindProc("ez_cvr_vqd_unload")
if err != nil {
VQDUnInit()
return
}
vqdCreate, err = vqdDLL.FindProc("ez_cvr_vqd_create")
if err != nil {
VQDUnInit()
return
}
vqdDestroy, err = vqdDLL.FindProc("ez_cvr_vqd_destroy")
if err != nil {
VQDUnInit()
return
}
vqdConfig, err = vqdDLL.FindProc("ez_cvr_vqd_config")
if err != nil {
VQDUnInit()
return
}
vqdRun, err = vqdDLL.FindProc("ez_cvr_vqd_run")
if err != nil {
VQDUnInit()
return
}
vqdSDKDir = filepath.ToSlash(vqdSDKDir)
dir, err := syscall.BytePtrFromString(vqdSDKDir)
if err != nil {
VQDUnInit()
return err
}
var errmsg [128]C.char
r1, _, _ := vqdLoad.Call(uintptr(unsafe.Pointer(dir)), uintptr(len(vqdSDKDir)), uintptr(unsafe.Pointer(&errmsg[0])))
if r1 != 0 {
VQDUnInit()
resBuf1 := (*[128]byte)(unsafe.Pointer(&errmsg[0]))[:128:128]
return fmt.Errorf("%s", resBuf1)
}
slog.Info("vqd cms open ok")
return
}
func VQDUnInit() {
if vqdUnLoad != nil {
_, _, err := vqdUnLoad.Call()
if err != nil {
slog.Error("vqd cms stop Call err", "err", err)
}
}
vqdUnLoad = nil
if vqdDLL != nil {
err := vqdDLL.Release()
if err != nil {
slog.Error("vqd cms stop Release err", "err", err)
}
}
slog.Info("vqd cms stop ok")
vqdDLL = nil
}
func (v *VideoInfoVQD) Create(params VQDPara, enable int, interval int) error {
v.mu.Lock()
defer v.mu.Unlock()
v.Params = params
var stPara C.NXU_VQD_Para_S
setParams(&stPara, params, enable)
ret, _, _ := vqdCreate.Call(uintptr(unsafe.Pointer(&v.VQDHandle)))
if ret != 0 {
return fmt.Errorf("vqdCreate fail:%d", ret)
}
ret, _, _ = vqdConfig.Call(v.VQDHandle, uintptr(unsafe.Pointer(&stPara)), uintptr(interval))
if ret != 0 {
return fmt.Errorf("vqdConfig fail:%d", ret)
}
v.IsCreateSuccess = true
return nil
}
func (v *VideoInfoVQD) Config(params VQDPara, enable int, interval int) error {
v.mu.Lock()
defer v.mu.Unlock()
if !v.IsCreateSuccess {
return fmt.Errorf("vqd cms is not create")
}
v.Params = params
var stPara C.NXU_VQD_Para_S
setParams(&stPara, params, enable)
ret, _, _ := vqdConfig.Call(v.VQDHandle, uintptr(unsafe.Pointer(&stPara)), uintptr(interval))
if ret != 0 {
return fmt.Errorf("vqdConfig fail:%d", ret)
}
return nil
}
func setParams(stPara *C.NXU_VQD_Para_S, params VQDPara, enable int) {
if params.UseDeepLearning {
stPara.bUseDeepLearning = C.NXU_TRUE
} else {
stPara.bUseDeepLearning = C.NXU_FALSE
}
//enable := NXU_VQD_ENABLE_LGTDARK
// 设置参数
// 设置亮度检测
stPara.s32EnableFunc = C.NXU_S32(enable)
stPara.stLgtDarkPara.f32DarkThr = C.NXU_FLOAT(params.LgtDarkPara.DarkThr)
stPara.stLgtDarkPara.f32LightThr = C.NXU_FLOAT(params.LgtDarkPara.LightThr)
stPara.stLgtDarkPara.f32LgtDarkAbnNumRatio = C.NXU_FLOAT(params.LgtDarkPara.LgtDarkAbnNumRatio)
stPara.s32VecFrmNum = C.int(params.VecFrmNum)
//enable |= NXU_VQD_ENABLE_BLUE
// 设置蓝屏检测
stPara.stBluePara.f32BlueThr = C.NXU_FLOAT(params.BluePara.BlueThr)
stPara.stBluePara.f32BlueAbnNumRatio = C.NXU_FLOAT(params.BluePara.BlueAbnNumRatio)
//enable |= NXU_VQD_ENABLE_CLARITY
// 设置清晰度检测
stPara.stClarityPara.f32ClarityThr = C.NXU_FLOAT(params.ClarityPara.ClarityThr)
stPara.stClarityPara.f32ClarityAbnNumRatio = C.NXU_FLOAT(params.ClarityPara.ClarityAbnNumRatio)
//enable |= NXU_VQD_ENABLE_SHARK
// 设置抖动检测
stPara.stSharkPara.f32SharkThr = C.NXU_FLOAT(params.SharkPara.SharkThr)
stPara.stSharkPara.f32SharkAbnNumRatio = C.NXU_FLOAT(params.SharkPara.SharkAbnNumRatio)
//enable |= NXU_VQD_ENABLE_FREEZE
// 设置冻结检测
stPara.stFreezePara.f32FreezeThr = C.NXU_FLOAT(params.FreezePara.FreezeThr)
stPara.stFreezePara.f32FreezeAbnNumRatio = C.NXU_FLOAT(params.FreezePara.FreezeAbnNumRatio)
//enable |= NXU_VQD_ENABLE_COLOR
// 设置偏色检测
stPara.stColorPara.f32ColorThr = C.NXU_FLOAT(params.ColorPara.ColorThr)
stPara.stColorPara.f32ColorAbnNumRatio = C.NXU_FLOAT(params.ColorPara.ColorAbnNumRatio)
//enable |= NXU_VQD_ENABLE_OCCLUSION
// 设置遮挡检测
stPara.stOcclusionPara.f32OcclusionThr = C.NXU_FLOAT(params.OcclusionPara.OcclusionThr)
stPara.stOcclusionPara.f32OcclusionAbnNumRatio = C.NXU_FLOAT(params.OcclusionPara.OcclusionAbnNumRatio)
//enable |= NXU_VQD_ENABLE_NOISE
// 设置噪声检测参数
stPara.stNoisePara.f32NoiseThr = C.NXU_FLOAT(params.NoisePara.NoiseThr)
stPara.stNoisePara.f32NoiseAbnNumRatio = C.NXU_FLOAT(params.NoisePara.NoiseAbnNumRatio)
//enable |= NXU_VQD_ENABLE_CONTRAST
// 设置对比度检测
stPara.stContrastPara.f32CtraLowThr = C.NXU_FLOAT(params.ContrastPara.CtraLowThr)
stPara.stContrastPara.f32CtraHighThr = C.NXU_FLOAT(params.ContrastPara.CtraHighThr)
stPara.stContrastPara.f32CtraAbnNumRatio = C.NXU_FLOAT(params.ContrastPara.CtraAbnNumRatio)
//enable |= NXU_VQD_ENABLE_MOSAIC
// 设置马赛克检测
stPara.stMosaicPara.f32MosaicThr = C.NXU_FLOAT(params.MosaicPara.MosaicThr)
stPara.stMosaicPara.f32MosaicAbnNumRatio = C.NXU_FLOAT(params.MosaicPara.MosaicAbnNumRatio)
//enable |= NXU_VQD_ENABLE_FLOWER
// 设置花屏检测
stPara.stFlowerPara.f32FlowerThr = C.NXU_FLOAT(params.FlowerPara.FlowerThr)
stPara.stFlowerPara.f32FlowerAbnNumRatio = C.NXU_FLOAT(params.FlowerPara.FlowerAbnNumRatio)
}
func (v *VideoInfoVQD) Frame(data []byte, w, h, frameNum int, save_filename string, outResult *VQDResult) int {
v.mu.Lock()
defer v.mu.Unlock()
if !v.IsCreateSuccess {
slog.Info("vqd cms is not create")
return -10
}
var result C.NXU_VQD_Result_S
yuv := C.CBytes(data)
defer C.free(yuv)
filename, _ := syscall.BytePtrFromString(save_filename)
ret, _, _ := vqdRun.Call(v.VQDHandle, uintptr(w), uintptr(h), uintptr(yuv), uintptr(frameNum), uintptr(unsafe.Pointer(filename)), uintptr(len(save_filename)), uintptr(unsafe.Pointer(&result)))
if ret == 0 {
// 分析成功
outResult.AbnormalType = int(result.s32AbnormalType)
outResult.ColorDev = float32(result.f32ColorDev)
outResult.LgtDark = float32(result.f32LgtDark)
outResult.Clarity = float32(result.f32Clarity)
outResult.Noise = float32(result.f32Noise)
outResult.Contrast = float32(result.f32Contrast)
outResult.Occlusion = float32(result.f32Occlusion)
outResult.Blue = float32(result.f32Blue)
outResult.Shark = float32(result.f32Shark)
outResult.Freeze = float32(result.f32Freeze)
outResult.Mosaic = float32(result.f32Mosaic)
outResult.Flower = float32(result.f32Flower)
}
return int(ret)
}
func (v *VideoInfoVQD) Destroy() {
v.mu.Lock()
defer v.mu.Unlock()
if !v.IsCreateSuccess {
slog.Error("vqd cms Destroy fail")
return
}
v.IsCreateSuccess = false
_, _, err := vqdDestroy.Call(uintptr(unsafe.Pointer(&v.VQDHandle)))
if err != nil {
slog.Error("vqd cms Destroy Call", err, err)
return
}
}

View File

@ -31,8 +31,6 @@ export async function GetVqdTaskTemplateById(id: string) {
*/ */
export async function UpdateVqdTaskTemplate(data: UpdateVqdTaskTemplateReq) { export async function UpdateVqdTaskTemplate(data: UpdateVqdTaskTemplateReq) {
const { id, ...payload } = data; const { id, ...payload } = data;
console.log(data);
return await PUT<VqdTaskTemplateBaseRes>(`/template/${id}`, payload); return await PUT<VqdTaskTemplateBaseRes>(`/template/${id}`, payload);
} }

View File

@ -0,0 +1,43 @@
import { GET, POST, PUT, DELETE } from "./http";
import type { VqdTimeTemplateBaseRes, VqdTimeTemplateRes, CreateVqdTimeTemplateReq, UpdateVqdTimeTemplateReq, VqdTimeTemplateDetailRes, VqdTimeTemplateReq } from "../types/vqdtimetemplate";
/**
*
* @returns
*/
export async function GetVqdTimeTemplate(data: VqdTimeTemplateReq) {
return await GET<VqdTimeTemplateRes>(`/plan`, data);
}
/**
*
* @param data
*/
export async function CreateVqdTimeTemplate(data: CreateVqdTimeTemplateReq) {
return await POST<VqdTimeTemplateBaseRes>(`/plan`, data);
}
/**
*
* @param id ID
*/
export async function GetVqdTimeTemplateById(id: string) {
return await GET<VqdTimeTemplateDetailRes>(`/plan/${id}`);
}
/**
*
* @param data id
*/
export async function UpdateVqdTimeTemplate(data: UpdateVqdTimeTemplateReq) {
const { id, ...payload } = data;
return await PUT<VqdTimeTemplateBaseRes>(`/plan/${id}`, payload);
}
/**
*
* @param id ID
*/
export async function DeleteVqdTimeTemplate(id: number) {
return await DELETE<VqdTimeTemplateBaseRes>(`/plan/${id}`);
}

View File

@ -1,8 +1,9 @@
import { forwardRef, useImperativeHandle, useState, useRef } from "react"; import { forwardRef, useImperativeHandle, useState, useRef } from "react";
import { Modal, Form, Input, Select, Button, message, Space } from "antd"; import { Modal, Form, Input, Select, Button, message, Space, Switch } from "antd";
import { useMutation, useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { CreateVqdTask, UpdateVqdTask } from "../api/vqdtask"; import { CreateVqdTask, UpdateVqdTask } from "../api/vqdtask";
import { GetVqdTaskTemplate } from "../api/vqdtasktemplate"; import { GetVqdTaskTemplate } from "../api/vqdtasktemplate";
import { GetVqdTimeTemplate } from "../api/vqdtimetemplate";
import type { CreateVqdTaskReq, VqdTaskItem } from "../types/vqdtask"; import type { CreateVqdTaskReq, VqdTaskItem } from "../types/vqdtask";
import { useGlobal } from "../Context"; import { useGlobal } from "../Context";
import ChannelModel, { IChannelModelFunc } from "./channel/Channel"; import ChannelModel, { IChannelModelFunc } from "./channel/Channel";
@ -42,9 +43,9 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
} else { } else {
setEditing(false); setEditing(false);
form.resetFields(); form.resetFields();
// form.setFieldsValue({ form.setFieldsValue({
// bid: "2", enable: true,
// }); });
} }
setOpen(true); setOpen(true);
}, },
@ -55,11 +56,9 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
name: "" name: ""
}); });
// 获取任务列表 // 获取模板列表
const { const {
data: storageResponse, data: storageResponse,
isLoading,
refetch,
} = useQuery({ } = useQuery({
queryKey: ["storage", pagination], queryKey: ["storage", pagination],
queryFn: () => queryFn: () =>
@ -72,6 +71,21 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
// refetchInterval: 4000, // refetchInterval: 4000,
retry: 1, retry: 1,
}); });
// 获取计划列表
const {
data: storageTimeResponse,
} = useQuery({
queryKey: ["storages", pagination],
queryFn: () =>
GetVqdTimeTemplate({ ...pagination })
.then((res) => res.data)
.catch((err) => {
ErrorHandle(err);
throw err;
}),
// refetchInterval: 4000,
retry: 1,
});
const { mutate: createMutate, isPending: creating } = useMutation({ const { mutate: createMutate, isPending: creating } = useMutation({
mutationFn: CreateVqdTask, mutationFn: CreateVqdTask,
onSuccess: () => { onSuccess: () => {
@ -101,7 +115,7 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
return ( return (
<Modal <Modal
style={{ top: '-180px' }} // 距离顶部 80px可改为 10% 等百分比) // style={{ top: '-180px' }} // 距离顶部 80px可改为 10% 等百分比)
title={title} title={title}
destroyOnHidden={true} destroyOnHidden={true}
open={open} open={open}
@ -123,7 +137,7 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
channel_id, channel_id,
channel_name, channel_name,
task_template_id, task_template_id,
task_template_name, time_template_id,
enable } = values as { enable } = values as {
name: string; name: string;
des: string; des: string;
@ -131,7 +145,7 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
channel_id: string; channel_id: string;
channel_name: string; channel_name: string;
task_template_id: number; task_template_id: number;
task_template_name: string; time_template_id: number;
enable: boolean; enable: boolean;
}; };
const payload: CreateVqdTaskReq = { const payload: CreateVqdTaskReq = {
@ -140,7 +154,7 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
channel_id, channel_id,
channel_name, channel_name,
task_template_id, task_template_id,
task_template_name, time_template_id,
enable, enable,
}; };
@ -174,7 +188,7 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
}}></Button> }}></Button>
</Space.Compact> </Space.Compact>
</Form.Item> </Form.Item>
<Form.Item name="task_template_id" label="选择模板" rules={[{ required: true, message: "请选择模板" }]}> <Form.Item name="task_template_id" label="关联模板" rules={[{ required: true, message: "请选择模板" }]}>
<Select <Select
placeholder="请选择模板" placeholder="请选择模板"
onChange={(res, item: any) => { onChange={(res, item: any) => {
@ -189,12 +203,35 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
></Select> ></Select>
</Form.Item> </Form.Item>
<Form.Item name="time_template_id" label="关联计划" rules={[{ required: true, message: "请选择计划" }]}>
<Select
placeholder="请选择计划"
onChange={(res, item: any) => {
form.setFieldsValue({ task_template_name: item?.label });
}}
options={storageTimeResponse?.items
.map((item) => ({
label: item.name,
value: item.id,
}))
.filter((item) => item.value !== 0)}
></Select>
</Form.Item>
<Form.Item <Form.Item
name="des" name="des"
label="描述" label="描述"
> >
<Input placeholder="请输入描述" /> <Input placeholder="请输入描述" />
</Form.Item> </Form.Item>
<Form.Item
style={{ width: '20%' }}
name="enable"
label="启用"
>
<Switch />
</Form.Item>
{editing && ( {editing && (
<Form.Item name="id" hidden> <Form.Item name="id" hidden>
<Input /> <Input />
@ -203,9 +240,6 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
<Form.Item name="channel_name" hidden> <Form.Item name="channel_name" hidden>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item name="task_template_name" hidden>
<Input />
</Form.Item>
</Form> </Form>
<ChannelModel ref={channelRef} onCallback={(id: any, name: any) => { <ChannelModel ref={channelRef} onCallback={(id: any, name: any) => {
form.setFieldsValue({ channel_id: id, channel_name: name }); form.setFieldsValue({ channel_id: id, channel_name: name });

View File

@ -1,63 +1,18 @@
import { forwardRef, useImperativeHandle, useState, useRef, createContext, useContext, useEffect } from "react"; import { forwardRef, useImperativeHandle, useState, createContext, useContext } from "react";
import { Modal, Form, Input, InputNumber, Button, message, Row, Col, Card, Flex, Switch, Tabs, FormInstance } from "antd"; import { Modal, Form, Input, InputNumber, message, Flex, Switch, FormInstance } from "antd";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import { GetVqdConfigDefault } from "../api/config"; import { GetVqdConfigDefault } from "../api/config";
import { CreateVqdTaskTemplate, UpdateVqdTaskTemplate, } from "../api/vqdtasktemplate"; import { CreateVqdTaskTemplate, UpdateVqdTaskTemplate, } from "../api/vqdtasktemplate";
import type { CreateVqdTaskTemplateReq, VqdTaskTemplateItem } from "../types/vqdtasktemplate"; import type { CreateVqdTaskTemplateReq, VqdTaskTemplateItem } from "../types/vqdtasktemplate";
import { useGlobal } from "../Context"; import { useGlobal } from "../Context";
import type { TabsProps } from 'antd';
const week = [
'星期一',
'星期二',
'星期三',
'星期四',
'星期五',
'星期六',
'星期日',
];
const hour = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23,
];
const gridStyle: React.CSSProperties = {
border: '0.1px solid #ccc',
textAlign: 'center',
lineHeight: '40px',
userSelect: 'none',
};
const titleStyle: React.CSSProperties = {
border: '0.1px solid #ccc',
width: '90px',
textAlign: 'center',
lineHeight: '40px',
userSelect: 'none',
};
type PlansSpan = {
start: string;
end: string;
};
export const emptyList = [
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
];
interface AddVqdTaskTemplateProps { interface AddVqdTaskTemplateProps {
title: string; title: string;
onSuccess: () => void; onSuccess: () => void;
} }
interface IAddTemplateContext { interface IAddTemplateContext {
editing: boolean; editing: boolean;
checkList: number[][];
form: FormInstance<CreateVqdTaskTemplateReq>; form: FormInstance<CreateVqdTaskTemplateReq>;
setCheckList: React.Dispatch<React.SetStateAction<number[][]>>;
onDelPullDeviceData: (index: number) => void; onDelPullDeviceData: (index: number) => void;
} }
const layout = { const layout = {
@ -80,54 +35,21 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
({ title, onSuccess }, ref) => { ({ title, onSuccess }, ref) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [editing, setEditing] = useState<boolean>(false); const [editing, setEditing] = useState<boolean>(false);
const [checkList, setCheckList] = useState<number[][]>([...emptyList.map((list) => [...list])]);
const [form] = Form.useForm(); const [form] = Form.useForm();
const { ErrorHandle } = useGlobal(); const { ErrorHandle } = useGlobal();
const arrayToString = (arr: number[][]): string => {
return arr.map((subArr) => subArr.join('')).join('');
}
const parsePlans = (value: string | undefined): number[][] => {
const result: number[][] = [];
if (!value) return result;
const binaryArray: number[] = value.split('').map(Number);
while (binaryArray.length) {
result.push(binaryArray.splice(0, 24));
}
return result;
};
const onChange = (key: string) => {
console.log(key);
};
const itemsTabs: TabsProps['items'] = [
{
key: '1',
forceRender: true,
label: '诊断参数',
children: <TemplateConfig />
},
{
key: '2',
forceRender: true,
label: '诊断时间',
children: <TemplatePlans />
},
];
const { mutate: getVqdConfigDefault } = useMutation({ const { mutate: getVqdConfigDefault } = useMutation({
mutationFn: GetVqdConfigDefault, mutationFn: GetVqdConfigDefault,
onSuccess: (res) => { onSuccess: (res) => {
const formValues = { const formValues = {
name: '',
des: '',
vqd_config: { vqd_config: {
enable: false, enable: false,
frm_num: res.data.frm_num, frm_num: res.data.frm_num,
is_deep_learn: res.data.is_deep_learn, is_deep_learn: res.data.is_deep_learn,
}, },
enable: true,
plans: '',
des: '',
name: '',
vqd_lgt_dark: res.data.vqd_lgt_dark, vqd_lgt_dark: res.data.vqd_lgt_dark,
vqd_blue: res.data.vqd_blue, vqd_blue: res.data.vqd_blue,
vqd_clarity: res.data.vqd_clarity, vqd_clarity: res.data.vqd_clarity,
@ -162,8 +84,6 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
const formValues = { const formValues = {
name: task.name, name: task.name,
id: task.id, id: task.id,
plans: task.plans,
enable: task.enable,
des: task.des, des: task.des,
vqd_config: task.vqd_config, vqd_config: task.vqd_config,
vqd_lgt_dark: task.vqd_lgt_dark, vqd_lgt_dark: task.vqd_lgt_dark,
@ -178,7 +98,6 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
vqd_mosaic: task.vqd_mosaic, vqd_mosaic: task.vqd_mosaic,
vqd_flower: task.vqd_flower, vqd_flower: task.vqd_flower,
}; };
setCheckList(parsePlans(task.plans))
form.setFieldsValue(formValues); form.setFieldsValue(formValues);
} else { } else {
getVqdConfigDefault() getVqdConfigDefault()
@ -224,9 +143,7 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
<AddTemplateContext.Provider <AddTemplateContext.Provider
value={{ value={{
form, form,
checkList,
editing, editing,
setCheckList,
onDelPullDeviceData onDelPullDeviceData
}} }}
> >
@ -247,7 +164,7 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
onFinish={(values: CreateVqdTaskTemplateReq) => { onFinish={(values: CreateVqdTaskTemplateReq) => {
if (creating || updating) return if (creating || updating) return
const payload = values as CreateVqdTaskTemplateReq; const payload = values as CreateVqdTaskTemplateReq;
payload.plans = arrayToString(checkList)
console.log(payload); console.log(payload);
if (editing) { if (editing) {
const id = (values as any).id; const id = (values as any).id;
@ -276,17 +193,9 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
> >
<Input placeholder="请输入描述" /> <Input placeholder="请输入描述" />
</Form.Item> </Form.Item>
<Form.Item
style={{ width: '20%' }}
name="enable"
label="启用"
>
<Switch />
</Form.Item>
</Flex> </Flex>
<Tabs defaultActiveKey="1" items={itemsTabs} onChange={onChange} /> <TemplateConfig />
{editing && ( {editing && (
<Form.Item name="id" hidden> <Form.Item name="id" hidden>
@ -302,182 +211,6 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
export default AddVqdTaskTemplate; export default AddVqdTaskTemplate;
const TemplatePlans: React.FC = () => {
const {
checkList,
editing,
setCheckList,
form,
} = useAddTemplate();
// 开始滑动选择
const [config, setConfig] = useState<{
start: boolean;
}>({ start: false });
const selector = useRef<boolean>(true);
const [coping, setCoping] = useState<
{ index: number; value: number[] } | undefined
>();
const handler = (list: number[]) => {
let out: PlansSpan[] = [];
let start = false;
let timeParam: PlansSpan = { start: '', end: '' };
for (let i = 0; i < list.length; i++) {
const v = list[i];
let s = i.toString().padStart(2, '0');
if (i == 23 && v == 1) {
s = '24';
}
if (!start && v == 1) {
start = true;
timeParam = { start: `${i}:00`, end: '' };
}
if (start && (v == 0 || i == list.length - 1)) {
start = false;
timeParam.end = `${s}:00`;
out.push(timeParam);
}
}
return out;
};
return <>
<div style={{ border: '1px solid #999' }}>
<Row wrap={false}>
<div
style={{
...titleStyle,
}}
>
Week/Time
</div>
<Row wrap={false} style={{ width: '86.5%' }}>
{[...hour].map((v, idx) => {
return (
<Col key={v} span={1} style={gridStyle}>
{v}
</Col>
);
})}
</Row>
</Row>
{week.map((v, weekIdx) => {
return (
<Row
key={v}
onMouseDown={() => setConfig({ ...config, start: true })}
onMouseUp={() => setConfig({ ...config, start: false })}
wrap={false}
>
<div style={titleStyle}>{v}</div>
<Row key={v + '1'} wrap={false} style={{ width: '86.5%' }}>
{[...hour].map((v, hourIdx) => {
// 时间选择
return (
<Col
onMouseOver={() => {
if (!config.start) return;
setCheckList((v) => {
let list = [...v];
list[weekIdx][hourIdx] = selector.current ? 1 : 0;
return list;
});
}}
onMouseDown={() => {
selector.current = checkList[weekIdx][hourIdx] == 0;
setCheckList((v) => {
let list = [...v];
list[weekIdx][hourIdx] = selector.current ? 1 : 0;
return list;
});
}}
key={v}
span={1}
style={{
...gridStyle,
backgroundColor:
checkList[weekIdx][hourIdx] == 1
? '#658EE0'
: 'white',
}}
></Col>
);
})}
<div
style={{
margin: 'auto',
marginLeft: '2px',
}}
>
<Button
type={coping?.index == weekIdx ? 'primary' : 'default'}
size="small"
style={{ padding: '1px 5px', fontSize: '12px' }}
onClick={() => {
if (!coping) {
setCoping({
index: weekIdx,
value: checkList[weekIdx],
});
return;
}
setCheckList((v) => {
let list = [...v];
list[weekIdx] = [...coping.value];
return list;
});
setCoping(undefined);
}}
>
{!coping || coping?.index == weekIdx ? "复制" : "粘贴"}
</Button>
</div>
</Row>
</Row>
);
})}
</div>
<div style={{ height: '20px' }}></div>
<Card>
<div>
<span></span>
</div>
<div>
{week.map((v, weekIdx) => {
return (
// 周
<Row wrap={false} key={v}>
<div style={{ color: '#9A9A9A', paddingRight: '5px' }}>
<span>{v}</span> :
</div>
<Row wrap={false}>
{handler(checkList[weekIdx]).map((v, idx, arr) => {
// 已选时间段
return (
<Col key={`${v.start}~${v.end}_${idx}_${weekIdx}`}>
{`${v.start}~${v.end}` +
(idx < arr.length - 1 ? ',' : '')}
</Col>
);
})}
</Row>
</Row>
);
})}
</div>
</Card>
</>
}
const BoxInputNumber: React.FC<{ const BoxInputNumber: React.FC<{
parent: string; parent: string;
children: string; children: string;

View File

@ -0,0 +1,450 @@
import { forwardRef, useImperativeHandle, useState, useRef, createContext, useContext, useEffect } from "react";
import { Modal, Form, Input, InputNumber, Button, message, Row, Col, Card, Flex, Switch, Tabs, FormInstance } from "antd";
import { useMutation } from "@tanstack/react-query";
import { GetVqdConfigDefault } from "../api/config";
import { CreateVqdTimeTemplate, UpdateVqdTimeTemplate, } from "../api/vqdtimetemplate";
import type { CreateVqdTimeTemplateReq, VqdTimeTemplateItem } from "../types/vqdtimetemplate";
import { useGlobal } from "../Context";
const week = [
'星期一',
'星期二',
'星期三',
'星期四',
'星期五',
'星期六',
'星期日',
];
const hour = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23,
];
const gridStyle: React.CSSProperties = {
border: '0.1px solid #ccc',
textAlign: 'center',
lineHeight: '40px',
userSelect: 'none',
};
const titleStyle: React.CSSProperties = {
border: '0.1px solid #ccc',
width: '90px',
textAlign: 'center',
lineHeight: '40px',
userSelect: 'none',
};
type PlansSpan = {
start: string;
end: string;
};
export const emptyList = [
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
Array(24).fill(0),
];
interface AddVqdTimeTemplateProps {
title: string;
onSuccess: () => void;
}
interface IAddTemplateContext {
editing: boolean;
checkList: number[][];
form: FormInstance<CreateVqdTimeTemplateReq>;
setCheckList: React.Dispatch<React.SetStateAction<number[][]>>;
onDelPullDeviceData: (index: number) => void;
}
const layout = {
labelCol: { span: 10 },
wrapperCol: { span: 14 },
};
const AddTemplateContext = createContext<IAddTemplateContext | null>(null);
const useAddTemplate = () => {
const context = useContext(AddTemplateContext);
if (!context) {
throw new Error('useAddTemplate must be used within a AddTemplateProvider');
}
return context;
};
export interface AddVqdTimeTemplateRef {
open: (task?: VqdTimeTemplateItem) => void;
}
const AddVqdTimeTemplate = forwardRef<AddVqdTimeTemplateRef, AddVqdTimeTemplateProps>(
({ title, onSuccess }, ref) => {
const [open, setOpen] = useState(false);
const [editing, setEditing] = useState<boolean>(false);
const [checkList, setCheckList] = useState<number[][]>([...emptyList.map((list) => [...list])]);
const [form] = Form.useForm();
const { ErrorHandle } = useGlobal();
const arrayToString = (arr: number[][]): string => {
return arr.map((subArr) => subArr.join('')).join('');
}
const parsePlans = (value: string | undefined): number[][] => {
const result: number[][] = [];
if (!value) return result;
const binaryArray: number[] = value.split('').map(Number);
while (binaryArray.length) {
result.push(binaryArray.splice(0, 24));
}
return result;
};
const onChange = (key: string) => {
console.log(key);
};
const { mutate: getVqdConfigDefault } = useMutation({
mutationFn: GetVqdConfigDefault,
onSuccess: (res) => {
const formValues = {
vqd_config: {
enable: false,
frm_num: res.data.frm_num,
is_deep_learn: res.data.is_deep_learn,
},
enable: true,
plans: '',
des: '',
name: '',
vqd_lgt_dark: res.data.vqd_lgt_dark,
vqd_blue: res.data.vqd_blue,
vqd_clarity: res.data.vqd_clarity,
vqd_shark: res.data.vqd_shark,
vqd_freeze: res.data.vqd_freeze,
vqd_color: res.data.vqd_color,
vqd_occlusion: res.data.vqd_occlusion,
vqd_noise: res.data.vqd_noise,
vqd_contrast: res.data.vqd_contrast,
vqd_mosaic: res.data.vqd_mosaic,
vqd_flower: res.data.vqd_flower,
};
formValues.vqd_lgt_dark.enable = false
formValues.vqd_blue.enable = false
formValues.vqd_clarity.enable = false
formValues.vqd_shark.enable = false
formValues.vqd_freeze.enable = false
formValues.vqd_color.enable = false
formValues.vqd_occlusion.enable = false
formValues.vqd_noise.enable = false
formValues.vqd_contrast.enable = false
formValues.vqd_mosaic.enable = false
formValues.vqd_flower.enable = false
form.setFieldsValue(formValues);
},
onError: ErrorHandle,
});
useImperativeHandle(ref, () => ({
open: (task?: VqdTimeTemplateItem) => {
if (task) {
setEditing(true);
const formValues = {
name: task.name,
id: task.id,
plans: task.plans,
enable: task.enable,
des: task.des,
};
setCheckList(parsePlans(task.plans))
form.setFieldsValue(formValues);
} else {
getVqdConfigDefault()
setEditing(false);
form.resetFields();
form.setFieldsValue({
});
}
setOpen(true);
},
}));
const { mutate: createMutate, isPending: creating } = useMutation({
mutationFn: CreateVqdTimeTemplate,
onSuccess: () => {
message.success("创建任务成功");
handleClose();
onSuccess?.();
},
onError: ErrorHandle,
});
const { mutate: updateMutate, isPending: updating } = useMutation({
mutationFn: UpdateVqdTimeTemplate,
onSuccess: () => {
message.success("更新任务成功");
handleClose();
onSuccess?.();
},
onError: ErrorHandle,
});
const handleClose = () => {
setOpen(false);
setEditing(false);
setCheckList([...emptyList.map((list) => [...list])])
form.resetFields();
};
const onDelPullDeviceData = (index: number) => {
}
return (
<AddTemplateContext.Provider
value={{
form,
checkList,
editing,
setCheckList,
onDelPullDeviceData
}}
>
<Modal
// style={{ top: '-180px' }} // 距离顶部 80px可改为 10% 等百分比)
title={title}
width={"64%"}
open={open}
onCancel={handleClose}
destroyOnHidden={true}
centered
onOk={() => form.submit()}
confirmLoading={creating || updating}
>
<Form
form={form}
{...layout}
onFinish={(values: CreateVqdTimeTemplateReq) => {
if (creating || updating) return
const payload = values as CreateVqdTimeTemplateReq;
payload.plans = arrayToString(checkList)
console.log(payload);
if (editing) {
const id = (values as any).id;
updateMutate({ id: String(id), ...payload });
} else {
createMutate(payload);
}
}}
>
<br />
<Flex gap="large" >
<Form.Item
style={{ width: '30%' }}
name="name"
label="计划名称"
rules={[{ required: true, message: "请输入计划名称" }]}
>
<Input placeholder="请输入名称" />
</Form.Item>
<Form.Item
style={{ width: '30%' }}
name="des"
label="描述"
>
<Input placeholder="请输入描述" />
</Form.Item>
<Form.Item
style={{ width: '20%' }}
name="enable"
label="启用"
>
<Switch />
</Form.Item>
</Flex>
<TemplatePlans />
{editing && (
<Form.Item name="id" hidden>
<Input />
</Form.Item>
)}
</Form>
</Modal>
</AddTemplateContext.Provider>
);
}
);
export default AddVqdTimeTemplate;
const TemplatePlans: React.FC = () => {
const {
checkList,
editing,
setCheckList,
form,
} = useAddTemplate();
// 开始滑动选择
const [config, setConfig] = useState<{
start: boolean;
}>({ start: false });
const selector = useRef<boolean>(true);
const [coping, setCoping] = useState<
{ index: number; value: number[] } | undefined
>();
const handler = (list: number[]) => {
let out: PlansSpan[] = [];
let start = false;
let timeParam: PlansSpan = { start: '', end: '' };
for (let i = 0; i < list.length; i++) {
const v = list[i];
let s = i.toString().padStart(2, '0');
if (i == 23 && v == 1) {
s = '24';
}
if (!start && v == 1) {
start = true;
timeParam = { start: `${i}:00`, end: '' };
}
if (start && (v == 0 || i == list.length - 1)) {
start = false;
timeParam.end = `${s}:00`;
out.push(timeParam);
}
}
return out;
};
return <>
<div style={{ border: '1px solid #999' }}>
<Row wrap={false}>
<div
style={{
...titleStyle,
}}
>
Week/Time
</div>
<Row wrap={false} style={{ width: '86.5%' }}>
{[...hour].map((v, idx) => {
return (
<Col key={v} span={1} style={gridStyle}>
{v}
</Col>
);
})}
</Row>
</Row>
{week.map((v, weekIdx) => {
return (
<Row
key={v}
onMouseDown={() => setConfig({ ...config, start: true })}
onMouseUp={() => setConfig({ ...config, start: false })}
wrap={false}
>
<div style={titleStyle}>{v}</div>
<Row key={v + '1'} wrap={false} style={{ width: '86.5%' }}>
{[...hour].map((v, hourIdx) => {
// 时间选择
return (
<Col
onMouseOver={() => {
if (!config.start) return;
setCheckList((v) => {
let list = [...v];
list[weekIdx][hourIdx] = selector.current ? 1 : 0;
return list;
});
}}
onMouseDown={() => {
selector.current = checkList[weekIdx][hourIdx] == 0;
setCheckList((v) => {
let list = [...v];
list[weekIdx][hourIdx] = selector.current ? 1 : 0;
return list;
});
}}
key={v}
span={1}
style={{
...gridStyle,
backgroundColor:
checkList[weekIdx][hourIdx] == 1
? '#658EE0'
: 'white',
}}
></Col>
);
})}
<div
style={{
margin: 'auto',
marginLeft: '2px',
}}
>
<Button
type={coping?.index == weekIdx ? 'primary' : 'default'}
size="small"
style={{ padding: '1px 5px', fontSize: '12px' }}
onClick={() => {
if (!coping) {
setCoping({
index: weekIdx,
value: checkList[weekIdx],
});
return;
}
setCheckList((v) => {
let list = [...v];
list[weekIdx] = [...coping.value];
return list;
});
setCoping(undefined);
}}
>
{!coping || coping?.index == weekIdx ? "复制" : "粘贴"}
</Button>
</div>
</Row>
</Row>
);
})}
</div>
<div style={{ height: '20px' }}></div>
<Card>
<div>
<span></span>
</div>
<div>
{week.map((v, weekIdx) => {
return (
// 周
<Row wrap={false} key={v}>
<div style={{ color: '#9A9A9A', paddingRight: '5px' }}>
<span>{v}</span> :
</div>
<Row wrap={false}>
{handler(checkList[weekIdx]).map((v, idx, arr) => {
// 已选时间段
return (
<Col key={`${v.start}~${v.end}_${idx}_${weekIdx}`}>
{`${v.start}~${v.end}` +
(idx < arr.length - 1 ? ',' : '')}
</Col>
);
})}
</Row>
</Row>
);
})}
</div>
</Card>
</>
}

View File

@ -9,13 +9,13 @@ import { useGlobal } from "../Context";
import { FormatFileSizeToString } from "../utils/rate"; import { FormatFileSizeToString } from "../utils/rate";
import { formatSecondsToHMS } from "../utils/time"; import { formatSecondsToHMS } from "../utils/time";
import Filter from "./Filter"; import Filter from "./Filter";
import AlarmSnap from './snap'; import AlarmSnap from './Snap';
const variants = ['filled'] as const; const variants = ['filled'] as const;
export default function VqdAlarmPage() { export default function VqdAlarmPage() {
const { ErrorHandle } = useGlobal(); const { ErrorHandle } = useGlobal();
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
page: 1, page: 1,
size: 10, size: 12,
name: "" name: ""
}); });
const [arrList, setArrList] = useState<any>([{ name: "全部类型", id: 0 }]); const [arrList, setArrList] = useState<any>([{ name: "全部类型", id: 0 }]);
@ -206,24 +206,25 @@ export default function VqdAlarmPage() {
}} }}
/> */} /> */}
<Flex justify="flex-end" className="mr-2" align="center"> <Flex justify="space-between" className="mr-2" align="center">
{/* <Space className="pl-2"> <Space className="pl-2">
<Popconfirm <Popconfirm
title="确定要批量删除文件吗?" title="确定要批量删除文件吗?"
onConfirm={() => { onConfirm={() => {
deleteMutationAll({ ids: selectedRowKeys as number[] }) const ids = storageResponse?.items.map(item => item.id);
deleteMutationAll({ ids: ids as number[] })
}} }}
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
> >
<Button color="danger" variant="solid" loading={delAllLoadings} disabled={selectedRowKeys.length == 0} > <Button color="danger" variant="solid" loading={delAllLoadings} >
</Button> </Button>
</Popconfirm> </Popconfirm>
</Space> */} </Space>
{/* <DatePicker {/* <DatePicker
defaultDate={moment().format('YYYY-MM-DD')} defaultDate={moment().format('YYYY-MM-DD')}
moonData={moonReq} moonData={moonReq}
@ -284,7 +285,7 @@ export default function VqdAlarmPage() {
title="删除告警" title="删除告警"
description="确定要删除此告警吗?" description="确定要删除此告警吗?"
onConfirm={() => { onConfirm={() => {
// delAlarmSnapshot([item.id]) deleteMutation(item.id)
}} }}
> >
{/* loading={isDelLoading} */} {/* loading={isDelLoading} */}

View File

@ -1,9 +1,9 @@
import { useRef, useState, useMemo } from "react"; import { useRef, useState, useMemo } from "react";
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip } from "antd"; import { Table, Button, Space, Popconfirm, Flex, message, Tooltip, Switch } from "antd";
import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons"; import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons";
import { useQuery, useMutation } from "@tanstack/react-query"; import { useQuery, useMutation } from "@tanstack/react-query";
import { GetVqdTask, DeleteVqdTask } from "../api/vqdtask"; import { GetVqdTask, DeleteVqdTask, UpdateVqdTask } from "../api/vqdtask";
import type { VqdTaskItem } from "../types/vqdtask"; import type { VqdTaskItem, CreateVqdTaskReq } from "../types/vqdtask";
import type { ColumnsType } from "antd/es/table"; import type { ColumnsType } from "antd/es/table";
import AddVqdTask, { AddVqdTaskRef } from "./AddVqdTask"; import AddVqdTask, { AddVqdTaskRef } from "./AddVqdTask";
import { useGlobal } from "../Context"; import { useGlobal } from "../Context";
@ -91,6 +91,21 @@ export default function VqdTaskPage() {
setSelectedRowKeys([...newSelectedRowKeys]); setSelectedRowKeys([...newSelectedRowKeys]);
}, },
}; };
const { mutate: updateMutate } = useMutation({
mutationFn: UpdateVqdTask,
onSuccess: () => {
message.success("更新成功");
setTimeout(() => {
refetch()
}, 500);
},
onError: ErrorHandle,
});
const saveTemplate = (disk: VqdTaskItem, t: boolean) => {
const payload = disk as CreateVqdTaskReq;
payload.enable = t
updateMutate({ id: String(disk.id), ...payload })
};
// 表格列定义 // 表格列定义
const columns: ColumnsType<VqdTaskItem> = [ const columns: ColumnsType<VqdTaskItem> = [
{ {
@ -109,7 +124,7 @@ export default function VqdTaskPage() {
), ),
}, },
{ {
title: "模板", title: "诊断模板",
dataIndex: "task_template_name", dataIndex: "task_template_name",
align: "center", align: "center",
render: (text, record) => ( render: (text, record) => (
@ -118,11 +133,33 @@ export default function VqdTaskPage() {
</Space> </Space>
), ),
}, },
{
title: "任务计划",
dataIndex: "time_template_name",
align: "center",
render: (text, record) => (
<Space>
{text}
</Space>
),
},
{ {
title: "描述", title: "描述",
dataIndex: "des", dataIndex: "des",
align: "center", align: "center",
}, },
{
title: "启用",
dataIndex: "enable",
align: "center",
render: (text, record) => (
<Space>
<Switch value={text} defaultChecked onChange={(t) => {
saveTemplate(record, t)
}} />
</Space>
),
},
{ {
title: "创建日期", title: "创建日期",
dataIndex: "created_at", dataIndex: "created_at",
@ -136,7 +173,7 @@ export default function VqdTaskPage() {
fixed: "right", fixed: "right",
render: (_, record) => ( render: (_, record) => (
<Space> <Space>
<Tooltip placement="top" title="编辑" color="#fff"> <Tooltip placement="top" title="编辑" color="#fff">
<Button icon={<EditOutlined />} onClick={() => handleEdit(record)} /> <Button icon={<EditOutlined />} onClick={() => handleEdit(record)} />
</Tooltip> </Tooltip>
@ -166,7 +203,7 @@ export default function VqdTaskPage() {
<Flex justify="space-between" align="center" className="mb-4"> <Flex justify="space-between" align="center" className="mb-4">
<Space> <Space>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}> <Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
</Button> </Button>
{/* <Popconfirm {/* <Popconfirm
title="确定要批量删除文件吗?" title="确定要批量删除文件吗?"
@ -213,7 +250,7 @@ export default function VqdTaskPage() {
{/* 编辑模态框 */} {/* 编辑模态框 */}
<AddVqdTask <AddVqdTask
ref={dialogRef} ref={dialogRef}
title="编辑" title="添加/编辑诊断任务"
onSuccess={() => refetch()} onSuccess={() => refetch()}
/> />
</div> </div>

View File

@ -64,11 +64,6 @@ export default function VqdTaskTemplatePage() {
const handleEdit = (disk: VqdTaskTemplateItem) => { const handleEdit = (disk: VqdTaskTemplateItem) => {
dialogRef.current?.open(disk); dialogRef.current?.open(disk);
}; };
const saveTemplate = (disk: VqdTaskTemplateItem, t: boolean) => {
const payload = disk as CreateVqdTaskTemplateReq;
payload.enable = t
updateMutate({ id: String(disk.id), ...payload })
};
// 处理分页变化 // 处理分页变化
const handleTableChange = (page: number, pageSize?: number) => { const handleTableChange = (page: number, pageSize?: number) => {
@ -111,18 +106,6 @@ export default function VqdTaskTemplatePage() {
dataIndex: "name", dataIndex: "name",
align: "center", align: "center",
}, },
{
title: "启用",
dataIndex: "enable",
align: "center",
render: (text, record) => (
<Space>
<Switch value={text} defaultChecked onChange={(t) => {
saveTemplate(record, t)
}} />
</Space>
),
},
{ {
title: "诊断模块", title: "诊断模块",
align: "center", align: "center",

View File

@ -0,0 +1,234 @@
import { useRef, useState, useMemo } from "react";
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip, Switch, Tag } from "antd";
import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons";
import { useQuery, useMutation } from "@tanstack/react-query";
import { GetVqdTimeTemplate, DeleteVqdTimeTemplate, UpdateVqdTimeTemplate } from "../api/vqdtimetemplate";
import type { VqdTimeTemplateItem, CreateVqdTimeTemplateReq } from "../types/vqdtimetemplate";
import type { ColumnsType } from "antd/es/table";
import AddVqdTimeTemplate, { AddVqdTimeTemplateRef } from "./AddVqdTimeTemplate";
import { useGlobal } from "../Context";
import { FormatFileSizeToString } from "../utils/rate";
import { formatSecondsToHMS } from "../utils/time";
import Filter from "./Filter";
export default function VqdTimeTemplatePage() {
const { ErrorHandle } = useGlobal();
const dialogRef = useRef<AddVqdTimeTemplateRef>(null);
const [pagination, setPagination] = useState({
page: 1,
size: 10,
name: ""
});
// 获取任务列表
const {
data: storageResponse,
isLoading,
refetch,
} = useQuery({
queryKey: ["storage", pagination],
queryFn: () =>
GetVqdTimeTemplate({ ...pagination })
.then((res) => res.data)
.catch((err) => {
ErrorHandle(err);
throw err;
}),
// refetchInterval: 4000,
retry: 2,
});
// 删除任务
const [delLoadings, setDelLoadings] = useState<number[]>([]);
const { mutate: deleteMutation } = useMutation({
mutationFn: DeleteVqdTimeTemplate,
onMutate: (id: number) => {
setDelLoadings((prev) => [...prev, id]);
},
onSuccess: (_, ctx) => {
setDelLoadings((prev) => prev.filter((item) => item !== ctx));
message.success("删除成功");
refetch();
},
onError: (error: Error, ctx) => {
setDelLoadings((prev) => prev.filter((item) => item !== ctx));
ErrorHandle(error);
},
});
// 打开新增模态框
const handleAdd = () => {
dialogRef.current?.open();
};
// 打开编辑模态框
const handleEdit = (disk: VqdTimeTemplateItem) => {
dialogRef.current?.open(disk);
};
const saveTemplate = (disk: VqdTimeTemplateItem, t: boolean) => {
const payload = disk as CreateVqdTimeTemplateReq;
payload.enable = t
updateMutate({ id: String(disk.id), ...payload })
};
// 处理分页变化
const handleTableChange = (page: number, pageSize?: number) => {
setPagination((prev) => ({
...prev,
page: page,
size: pageSize || prev.size,
}));
};
// 客户端分页数据
const dataSource = useMemo(() => {
const items = storageResponse?.items || [];
const start = (pagination.page - 1) * pagination.size;
const end = start + pagination.size;
return items.slice(start, end);
}, [storageResponse, pagination]);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const rowSelection = {
selectedRowKeys,
onChange: (
newSelectedRowKeys: React.Key[],
selectedRows: VqdTimeTemplateItem[]
) => {
setSelectedRowKeys([...newSelectedRowKeys]);
},
};
const { mutate: updateMutate } = useMutation({
mutationFn: UpdateVqdTimeTemplate,
onSuccess: () => {
message.success("更新成功");
setTimeout(() => {
refetch()
}, 500);
},
onError: ErrorHandle,
});
// 表格列定义
const columns: ColumnsType<VqdTimeTemplateItem> = [
{
title: "名称",
dataIndex: "name",
align: "center",
},
{
title: "启用",
dataIndex: "enable",
align: "center",
render: (text, record) => (
<Space>
<Switch value={text} defaultChecked onChange={(t) => {
saveTemplate(record, t)
}} />
</Space>
),
},
{
title: "描述",
dataIndex: "des",
align: "center",
},
// {
// title: "日期",
// dataIndex: "created_at",
// align: "center",
// render: (text: string, record) => (
// <>
// <div>创建:{(text ? new Date(text).toLocaleString() : "-")}</div>
// 更新:{(record?.updated_at ? new Date(record?.updated_at).toLocaleString() : "-")}
// </>
// ),
// },
{
title: "操作",
align: "center",
width: 120,
fixed: "right",
render: (_, record) => (
<Space>
<Tooltip placement="top" title="编辑" color="#fff">
<Button icon={<EditOutlined />} onClick={() => handleEdit(record)} />
</Tooltip>
{!record.is_default && <Popconfirm
title="确定要删除这个模板吗?"
onConfirm={() => {
if (record.id) {
deleteMutation(record.id);
}
}}
okText="确定"
cancelText="取消"
>
<Button
loading={delLoadings.includes(record.id)}
danger
icon={<DeleteOutlined />}
/>
</Popconfirm>}
</Space>
),
},
];
return (
<div>
<Flex justify="space-between" align="center" className="mb-4">
<Space>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
</Button>
{/* <Popconfirm
title="确定要批量删除文件吗?"
onConfirm={() => {
}}
okText="确定"
cancelText="取消"
>
<Button color="danger" variant="solid">
</Button>
</Popconfirm> */}
</Space>
<Filter
searchLoading={isLoading}
onSearchChange={(value: string) => {
setPagination({ ...pagination, name: value });
}}
/>
</Flex>
{/* 表格 */}
<Table
columns={columns}
// rowSelection={rowSelection}
dataSource={storageResponse?.items}
rowKey="id"
loading={isLoading}
scroll={{ x: "max-content" }}
pagination={{
current: pagination.page,
pageSize: pagination.size,
total: storageResponse?.total || 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${total}`,
onChange: handleTableChange,
onShowSizeChange: handleTableChange,
}}
/>
{/* 编辑模态框 */}
<AddVqdTimeTemplate
ref={dialogRef}
title="添加/编辑模板"
onSuccess={() => refetch()}
/>
</div>
);
}

View File

@ -2,13 +2,15 @@ import { Affix, Col, Menu, Row, type MenuProps } from "antd";
import { import {
FileSearchOutlined, FileSearchOutlined,
AlertOutlined, AlertOutlined,
FileTextOutlined, CarryOutOutlined,
SettingOutlined SettingOutlined,
FileProtectOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import Box from "../components/Box"; import Box from "../components/Box";
import { useState } from "react"; import { useState } from "react";
import VqdTaskPage from "../components/VqdTask"; import VqdTaskPage from "../components/VqdTask";
import VqdTaskTemplatePage from "../components/VqdTaskTemplate"; import VqdTaskTemplatePage from "../components/VqdTaskTemplate";
import VqdTimeTemplatePage from "../components/VqdTimeTemplate";
import VqdAlarmPage from "../components/VqdAlarm"; import VqdAlarmPage from "../components/VqdAlarm";
import VqdConfigPage from "../components/VqdConfig"; import VqdConfigPage from "../components/VqdConfig";
@ -19,21 +21,26 @@ export default function Home() {
const items: MenuItem[] = [ const items: MenuItem[] = [
{ {
key: "sub0", key: "sub0",
label: "任务管理", label: "设备视频诊断",
icon: <FileSearchOutlined />, icon: <FileSearchOutlined />,
}, },
{ {
key: "sub1", key: "sub1",
label: "任务模板", label: "诊断结果查询",
icon: <FileTextOutlined />,
},
{
key: "sub2",
label: "诊断告警",
icon: <AlertOutlined />, icon: <AlertOutlined />,
}, },
{
key: "sub2",
label: "诊断计划",
icon: <CarryOutOutlined />,
},
{ {
key: "sub3", key: "sub3",
label: "诊断参数",
icon: <FileProtectOutlined />,
},
{
key: "sub4",
label: "基础配置", label: "基础配置",
icon: <SettingOutlined />, icon: <SettingOutlined />,
}, },
@ -82,15 +89,20 @@ export default function Home() {
)} )}
{currentMenu == "sub1" && ( {currentMenu == "sub1" && (
<Box> <Box>
<VqdTaskTemplatePage /> <VqdAlarmPage />
</Box> </Box>
)} )}
{currentMenu == "sub2" && ( {currentMenu == "sub2" && (
<Box> <Box>
<VqdAlarmPage /> <VqdTimeTemplatePage />
</Box> </Box>
)} )}
{currentMenu == "sub3" && ( {currentMenu == "sub3" && (
<Box>
<VqdTaskTemplatePage />
</Box>
)}
{currentMenu == "sub4" && (
<Box> <Box>
<VqdConfigPage /> <VqdConfigPage />
</Box> </Box>

View File

@ -27,7 +27,9 @@ export type VqdTaskItem = {
channel_id: string; channel_id: string;
channel_name: string; channel_name: string;
task_template_id: number; task_template_id: number;
time_template_id: number;
task_template_name: string; task_template_name: string;
time_template_name: string;
enable: boolean; enable: boolean;
created_at?: string; created_at?: string;
updated_at?: string; updated_at?: string;
@ -70,9 +72,9 @@ export type CreateVqdTaskReq = {
*/ */
task_template_id: number; task_template_id: number;
/** /**
* *
*/ */
task_template_name: string; time_template_id: number;
/** /**
* *
*/ */
@ -99,12 +101,14 @@ export type UpdateVqdTaskReq = Partial<CreateVqdTaskReq> & {
export type VqdTaskDetailRes = { export type VqdTaskDetailRes = {
id: number; id: number;
name: string; name: string;
size: number; channel_id: string;
mode: string; channel_name: string;
encode_status: number; task_template_id: number;
file_name: string; time_template_id: number;
source_url: string; task_template_name: string;
time_template_name: string;
enable: boolean;
created_at?: string; created_at?: string;
updated_at?: string; updated_at?: string;
des?: string; des: string;
} }

View File

@ -24,8 +24,6 @@ export type VqdTaskTemplateRes = {
export type VqdTaskTemplateItem = { export type VqdTaskTemplateItem = {
id: number; id: number;
name: string; name: string;
plans: string;
enable: boolean;
is_default: boolean; is_default: boolean;
vqd_config: VqdConfig; vqd_config: VqdConfig;
vqd_lgt_dark: VqdLgtDark; vqd_lgt_dark: VqdLgtDark;
@ -131,14 +129,6 @@ export type CreateVqdTaskTemplateReq = {
* *
*/ */
name: string; name: string;
/**
*
*/
plans: string;
/**
*
*/
enable: boolean;
vqd_config: VqdConfig; vqd_config: VqdConfig;
vqd_lgt_dark: VqdLgtDark; vqd_lgt_dark: VqdLgtDark;
vqd_blue: VqdBlue; vqd_blue: VqdBlue;
@ -173,8 +163,6 @@ export type UpdateVqdTaskTemplateReq = Partial<CreateVqdTaskTemplateReq> & {
export type VqdTaskTemplateDetailRes = { export type VqdTaskTemplateDetailRes = {
id: number; id: number;
name: string; name: string;
plans: string;
enable: boolean;
is_default: boolean; is_default: boolean;
vqd_config: VqdConfig; vqd_config: VqdConfig;
vqd_lgt_dark: VqdLgtDark; vqd_lgt_dark: VqdLgtDark;

View File

@ -0,0 +1,95 @@
/**
*
*/
export type VqdTimeTemplateBaseRes = {
data: string;
};
/**
*
*/
export type VqdTimeTemplateRes = {
items: VqdTimeTemplateItem[];
/**
*
*/
total: number;
};
/**
*
*/
export type VqdTimeTemplateItem = {
id: number;
name: string;
plans: string;
enable: boolean;
is_default: boolean;
created_at?: string;
updated_at?: string;
des: string;
};
/**
*
*/
export type VqdTimeTemplateReq = {
/**
*
*/
name?: string;
/**
* (1~N)
*/
page: number;
/**
* (10~100)
*/
size: number;
}
export type CreateVqdTimeTemplateReq = {
/**
*
*/
name: string;
/**
*
*/
plans: string;
/**
*
*/
enable: boolean;
/**
*
*/
des: string;
};
/**
*
*/
export type UpdateVqdTimeTemplateReq = Partial<CreateVqdTimeTemplateReq> & {
/**
* ID
*/
id: string;
};
/**
* Apifox
*/
export type VqdTimeTemplateDetailRes = {
id: number;
name: string;
plans: string;
enable: boolean;
is_default: boolean;
created_at?: string;
updated_at?: string;
des: string;
}