package HikSDK /* #cgo LDFLAGS: -Wl,--allow-multiple-definition #include #include */ 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) }