Files
HikNetSDKPkg/BallCamera.go
2025-04-30 16:48:33 +08:00

439 lines
11 KiB
Go

package HikSDK
/*
#cgo LDFLAGS: -Wl,--allow-multiple-definition
#include <stdio.h>
#include <string.h>
*/
import "C"
import (
"encoding/binary"
"errors"
"fmt"
"gitea.com/kunmeng/HikNetSDKPkg/Core"
"sync"
"time"
"unsafe"
)
const (
Base = 0
BuKongQiu = 1
)
type PTZEnumObj struct {
PTZ_LEFT int
PTZ_RIGHT int
PTZ_UP int
PTZ_DOWN int
PTZ_UP_LEFT int
PTZ_UP_RIGHT int
PTZ_DOWN_LEFT int
PTZ_DOWN_RIGHT int
PTZ_ZOOM_IN int
PTZ_ZOOM_OUT int
PTZ_Focus_Far int
PTZ_Focus_Near int
}
func (receiver *PTZEnumObj) toHikPTZEnum(v int) int {
switch v {
case PTZEnum.PTZ_LEFT:
return HikPTZEnum.PAN_LEFT
case PTZEnum.PTZ_RIGHT:
return HikPTZEnum.PAN_RIGHT
case PTZEnum.PTZ_UP:
return HikPTZEnum.TILT_UP
case PTZEnum.PTZ_DOWN:
return HikPTZEnum.TILT_DOWN
case PTZEnum.PTZ_ZOOM_IN:
return HikPTZEnum.ZOOM_IN
case PTZEnum.PTZ_ZOOM_OUT:
return HikPTZEnum.ZOOM_OUT
case PTZEnum.PTZ_Focus_Far:
return HikPTZEnum.FOCUS_FAR
case PTZEnum.PTZ_Focus_Near:
return HikPTZEnum.FOCUS_NEAR
case PTZEnum.PTZ_UP_LEFT:
return HikPTZEnum.UP_LEFT
case PTZEnum.PTZ_UP_RIGHT:
return HikPTZEnum.UP_RIGHT
case PTZEnum.PTZ_DOWN_LEFT:
return HikPTZEnum.DOWN_LEFT
case PTZEnum.PTZ_DOWN_RIGHT:
return HikPTZEnum.DOWN_RIGHT
default:
return -1
}
}
var PTZEnum = PTZEnumObj{
PTZ_LEFT: 1,
PTZ_RIGHT: 2,
PTZ_UP: 3,
PTZ_DOWN: 4,
PTZ_UP_LEFT: 5,
PTZ_UP_RIGHT: 6,
PTZ_DOWN_LEFT: 7,
PTZ_DOWN_RIGHT: 8,
PTZ_ZOOM_IN: 9,
PTZ_ZOOM_OUT: 10,
PTZ_Focus_Far: 11,
PTZ_Focus_Near: 12,
}
var HikPTZEnum = struct {
LIGHT_PWRON int //接通灯光电源
WIPER_PWRON int //接通雨刷开关
FAN_PWRON int //接通风扇开关
HEATER_PWRON int //接通加热器开关
AUX_PWRON1 int //接通辅助设备开关
AUX_PWRON2 int //接通辅助设备开关
ZOOM_IN int //焦距变大(倍率变大)
ZOOM_OUT int //焦距变小(倍率变小)
FOCUS_NEAR int //焦点前调
FOCUS_FAR int //焦点后调
IRIS_OPEN int //光圈扩大
IRIS_CLOSE int //光圈缩小
TILT_UP int //云台上仰
TILT_DOWN int //云台下俯
PAN_LEFT int //云台左转
PAN_RIGHT int //云台右转
UP_LEFT int //云台上仰和左转
UP_RIGHT int //云台上仰和右转
DOWN_LEFT int //云台下俯和左转
DOWN_RIGHT int //云台下俯和右转
PAN_AUTO int //云台左右自动扫描
TILT_DOWN_ZOOM_IN int //云台下俯和焦距变大(倍率变大)
TILT_DOWN_ZOOM_OUT int //云台下俯和焦距变小(倍率变小)
PAN_LEFT_ZOOM_IN int //云台左转和焦距变大(倍率变大)
PAN_LEFT_ZOOM_OUT int //云台左转和焦距变小(倍率变小)
PAN_RIGHT_ZOOM_IN int //云台右转和焦距变大(倍率变大)
PAN_RIGHT_ZOOM_OUT int //云台右转和焦距变小(倍率变小)
UP_LEFT_ZOOM_IN int //云台上仰和左转和焦距变大(倍率变大)
UP_LEFT_ZOOM_OUT int //云台上仰和左转和焦距变小(倍率变小)
UP_RIGHT_ZOOM_IN int //云台上仰和右转和焦距变大(倍率变大)
UP_RIGHT_ZOOM_OUT int //云台上仰和右转和焦距变小(倍率变小)
DOWN_LEFT_ZOOM_IN int //云台下俯和左转和焦距变大(倍率变大)
DOWN_LEFT_ZOOM_OUT int //云台下俯和左转和焦距变小(倍率变小)
DOWN_RIGHT_ZOOM_IN int //云台下俯和右转和焦距变大(倍率变大)
DOWN_RIGHT_ZOOM_OUT int //云台下俯和右转和焦距变小(倍率变小)
TILT_UP_ZOOM_IN int //云台上仰和焦距变大(倍率变大)
TILT_UP_ZOOM_OUT int //云台上仰和焦距变小(倍率变小)
}{LIGHT_PWRON: 2,
WIPER_PWRON: 3,
FAN_PWRON: 4,
HEATER_PWRON: 5,
AUX_PWRON1: 6,
AUX_PWRON2: 7,
ZOOM_IN: 11,
ZOOM_OUT: 12,
FOCUS_NEAR: 13,
FOCUS_FAR: 14,
IRIS_OPEN: 15,
IRIS_CLOSE: 16,
TILT_UP: 21,
TILT_DOWN: 22,
PAN_LEFT: 23,
PAN_RIGHT: 24,
UP_LEFT: 25,
UP_RIGHT: 26,
DOWN_LEFT: 27,
DOWN_RIGHT: 28,
PAN_AUTO: 29,
TILT_DOWN_ZOOM_IN: 58,
TILT_DOWN_ZOOM_OUT: 59,
PAN_LEFT_ZOOM_IN: 60,
PAN_LEFT_ZOOM_OUT: 61,
PAN_RIGHT_ZOOM_IN: 62,
PAN_RIGHT_ZOOM_OUT: 63,
UP_LEFT_ZOOM_IN: 64,
UP_LEFT_ZOOM_OUT: 65,
UP_RIGHT_ZOOM_IN: 66,
UP_RIGHT_ZOOM_OUT: 67,
DOWN_LEFT_ZOOM_IN: 68,
DOWN_LEFT_ZOOM_OUT: 69,
DOWN_RIGHT_ZOOM_IN: 70,
DOWN_RIGHT_ZOOM_OUT: 71,
TILT_UP_ZOOM_IN: 72,
TILT_UP_ZOOM_OUT: 73,
}
type BallCamera struct {
userId Core.LONG
_type uint8
deviceInfo Core.NET_DVR_DEVICEINFO_V30
mu sync.Mutex
expectedType byte
}
func NewBallCamera(Ip string, Port int, Username, Password string, Type uint8) (*BallCamera, error) {
UserId, DeviceInfo, err := Core.Login(Ip, Port, Username, Password)
if err != nil {
return nil, err
}
return &BallCamera{
userId: UserId,
_type: Type,
deviceInfo: DeviceInfo,
}, nil
}
type PTZ struct {
P float32
T float32
Z float32
}
func (this *BallCamera) GetPTZ() (PTZ, error) {
if this._type == BuKongQiu {
var data PTZ
ch := make(chan bool)
SerialStartHandle, err := Core.SerialStart(this.userId, func(lSerialHandle Core.LONG, lChannel Core.LONG, pRecvDataBuffer []byte, dwBufSize Core.DWORD, pUser unsafe.Pointer) {
if dwBufSize != 7 {
ch <- false
return
}
Type := pRecvDataBuffer[3]
this.mu.Lock()
expected := this.expectedType
this.mu.Unlock()
if Type != expected {
ch <- false
return
}
switch Type {
case 0x59:
data.P = float32(binary.BigEndian.Uint16(pRecvDataBuffer[4:6])) / 100.
case 0x5B:
data.T = float32(binary.BigEndian.Uint16(pRecvDataBuffer[4:6])) / 100.
case 0x5D:
data.Z = float32(binary.BigEndian.Uint16(pRecvDataBuffer[4:6])) / 100.
default:
ch <- false
}
ch <- true
})
if err != nil {
return data, err
}
defer func() {
err = Core.SerialStop(SerialStartHandle)
if err != nil {
println(err.Error())
}
time.Sleep(1 * time.Second)
}()
// 获取P值
this.mu.Lock()
this.expectedType = 0x59
this.mu.Unlock()
if err := this.retrySend(SerialStartHandle, []byte{0xff, 0x01, 0x00, 0x51, 0x00, 0x00, 0x52}, 5, ch); err != nil {
return data, fmt.Errorf("获取P值失败: %w", err)
}
this.mu.Lock()
this.expectedType = 0x5B
this.mu.Unlock()
if err := this.retrySend(SerialStartHandle, []byte{0xff, 0x01, 0x00, 0x53, 0x00, 0x00, 0x54}, 5, ch); err != nil {
return data, fmt.Errorf("获取T值失败: %w", err)
}
this.mu.Lock()
this.expectedType = 0x5D
this.mu.Unlock()
if err := this.retrySend(SerialStartHandle, []byte{0xff, 0x01, 0x00, 0x55, 0x00, 0x00, 0x56}, 5, ch); err != nil {
return data, fmt.Errorf("获取Z值失败: %w", err)
}
return data, nil
}
var data Core.CDVR_PTZPOS
var dataPtr = unsafe.Pointer(&data)
err := Core.GetDVRConfig(this.userId, 293, 1, dataPtr, Core.DWORD(unsafe.Sizeof(data)))
if err != nil {
return PTZ{}, err
}
res := data.Go()
return PTZ{
P: float32(res.WPanPos),
T: float32(res.WTiltPos),
Z: float32(res.WZoomPos),
}, nil
}
func padding(n int) ([]byte, error) {
if n < 0 || n > 65535 {
return []byte{0x00, 0x00}, errors.New("n must be in the range 0-65535")
}
return []byte{byte(n >> 8), byte(n & 0xFF)}, nil
}
func (this *BallCamera) PtzGotoPut(Action int, P, T, Z float64) error {
if this._type == BuKongQiu {
SerialStartHandle, err := Core.SerialStart(this.userId, func(lSerialHandle Core.LONG, lChannel Core.LONG, pRecvDataBuffer []byte, dwBufSize Core.DWORD, pUser unsafe.Pointer) {
})
if err != nil {
return err
}
defer func() {
err = Core.SerialStop(SerialStartHandle)
if err != nil {
println(err.Error())
}
time.Sleep(1 * time.Second)
}()
PByte, err := padding(int(P * 100))
if err != nil {
return err
}
TByte, err := padding(int(T * 100))
if err != nil {
return err
}
ZByte, err := padding(int(Z * 100))
if err != nil {
return err
}
pBuf := append([]byte{0xff, 0x01, 0x00, 0x4b}, PByte...)
tBuf := append([]byte{0xff, 0x01, 0x00, 0x4d}, TByte...)
zBuf := append([]byte{0xff, 0x01, 0x00, 0x4f}, ZByte...)
pBufv, err := verify(pBuf)
if err != nil {
return err
}
tBufv, err := verify(tBuf)
if err != nil {
return err
}
zBufv, err := verify(zBuf)
if err != nil {
return err
}
pBuf = append(pBuf, pBufv)
tBuf = append(tBuf, tBufv)
zBuf = append(zBuf, zBufv)
switch Action {
case 1:
err = Core.SerialSend(SerialStartHandle, pBuf)
if err != nil {
return err
}
err = Core.SerialSend(SerialStartHandle, tBuf)
if err != nil {
return err
}
err = Core.SerialSend(SerialStartHandle, zBuf)
if err != nil {
return err
}
break
case 2:
err = Core.SerialSend(SerialStartHandle, pBuf)
if err != nil {
return err
}
break
case 3:
err = Core.SerialSend(SerialStartHandle, tBuf)
if err != nil {
return err
}
break
case 4:
err = Core.SerialSend(SerialStartHandle, zBuf)
if err != nil {
return err
}
break
case 5:
err = Core.SerialSend(SerialStartHandle, pBuf)
if err != nil {
return err
}
err = Core.SerialSend(SerialStartHandle, tBuf)
if err != nil {
return err
}
break
default:
return errors.New("action error")
}
return nil
}
var data Core.CDVR_PTZPOS
data.Set(float64(Action), P, T, Z)
var dataPtr = unsafe.Pointer(&data)
err := Core.SetDVRConfig(this.userId, 292, 1, dataPtr, Core.DWORD(unsafe.Sizeof(data)))
if err != nil {
return err
}
return nil
}
func (this *BallCamera) retrySend(handle Core.LONG, cmd []byte, maxRetries int, ch <-chan bool) error {
for retry := 0; retry < maxRetries; retry++ {
if err := Core.SerialSend(handle, cmd); err != nil {
return err
}
select {
case success := <-ch:
if success {
return nil
}
if retry == maxRetries-1 {
return fmt.Errorf("达到最大重试次数 %d", maxRetries)
}
case <-time.After(2 * time.Second): // 添加超时机制
if retry == maxRetries-1 {
return fmt.Errorf("响应超时,重试 %d 次后失败", maxRetries)
}
}
}
return nil
}
func (receiver *BallCamera) StartBus(direction int, speed int) error {
err := Core.PTZControlWithSpeed_Other(receiver.userId, Core.LONG(receiver.deviceInfo.ByStartChan), Core.DWORD(PTZEnum.toHikPTZEnum(direction)), Core.DWORD(0), Core.DWORD(speed))
if err != nil {
return err
}
return nil
}
func (receiver *BallCamera) StopBus(direction int, speed int) error {
err := Core.PTZControlWithSpeed_Other(receiver.userId, Core.LONG(receiver.deviceInfo.ByStartChan), Core.DWORD(PTZEnum.toHikPTZEnum(direction)), Core.DWORD(1), Core.DWORD(speed))
if err != nil {
return err
}
return nil
}
func verify(data []byte) (byte, error) {
if len(data) < 6 {
return 0, fmt.Errorf("data too short")
}
sum := 0
for i := 1; i < 6; i++ {
sum += int(data[i])
}
// 取模并转换为16进制
checksum := sum % 0x100
return byte(checksum), nil
}
func (this *BallCamera) Logout() error {
return Core.Logout(this.userId)
}
func (this *BallCamera) Status() bool {
return Core.NET_DVR_RemoteControl(this.userId)
}