445 lines
11 KiB
Go
445 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
|
|
}
|
|
//println(PByte, TByte, ZByte)
|
|
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(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(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 (receiver *BallCamera) Logout() error {
|
|
//if receiver._type == BuKongQiu {
|
|
// err := core.SerialStop(receiver.serialStartHandle)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
//}
|
|
err := Core.Logout(receiver.userId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|