Files
HikNetSDKPkg/Core.go
2025-08-25 10:26:36 +08:00

481 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package HikSDK
/*
#cgo windows,amd64 LDFLAGS: -L${SRCDIR}/library/amd64_windows/lib -lHCCore -lHCNetSDK
#cgo linux,amd64 LDFLAGS: -L${SRCDIR}/library/amd64_linux/lib -lHCCore -lhcnetsdk
#cgo linux,arm64 LDFLAGS: -L${SRCDIR}/library/arm64_linux/lib -lHCCore -lhcnetsdk
#cgo windows,amd64 CFLAGS: -I${SRCDIR}/library/amd64_windows/include
#cgo linux,amd64 CFLAGS: -I${SRCDIR}/library/amd64_linux/include
#cgo linux,arm64 CFLAGS: -I${SRCDIR}/library/arm64_linux/include
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
void sleep_seconds(int seconds) {
#ifdef _WIN32
Sleep(seconds * 1000); // Sleep 参数是毫秒
#else
sleep(seconds); // sleep 参数是秒
#endif
}
void sys_sleep_seconds(int seconds) {
#ifdef _WIN32
Sleep(seconds * 1000); // Sleep 参数是毫秒
#endif
}
#include "HCNetSDK.h"
typedef struct DEVICEINFO
{
BYTE byChanNum;
BYTE byStartChan;
} DEVICEINFO;
typedef struct PTZ{
WORD P;
WORD T;
WORD Z;
} PTZ;
typedef struct TimeRange
{
DWORD StartYear;
DWORD StartMonth;
DWORD StartDay;
DWORD StartHour;
DWORD StartMinute;
DWORD StartSecond;
DWORD EndYear;
DWORD EndMonth;
DWORD EndDay;
DWORD EndHour;
DWORD EndMinute;
DWORD EndSecond;
} TimeRange;
LONG Login(char *sDVRIP,WORD wDVRPort,char *sUserName,char *sPassword,struct DEVICEINFO *info){
NET_DVR_DEVICEINFO hikInfo;
LONG lUserID = NET_DVR_Login(sDVRIP, wDVRPort, sUserName, sPassword,&hikInfo);
info->byChanNum = hikInfo.byChanNum;
info->byStartChan = hikInfo.byStartChan;
return lUserID;
}
int GetTimeZone(LONG lUserID){
NET_DVR_NETAPPCFG NTPData;
DWORD RESLEN;
NET_DVR_GetDVRConfig(lUserID,222,0,&NTPData,sizeof(NTPData),&RESLEN);
return NTPData.struNtpClientParam.cTimeDifferenceH*60 + NTPData.struNtpClientParam.cTimeDifferenceM*((NTPData.struNtpClientParam.cTimeDifferenceH>>7)?-1:1);
}
void GetPTZ(LONG lUserID,PTZ *info){
NET_DVR_PTZPOS PTZPOS;
DWORD RESLEN;
NET_DVR_GetDVRConfig(lUserID,293,1,&PTZPOS,sizeof(PTZPOS),&RESLEN);
info->P = PTZPOS.wPanPos;
info->T = PTZPOS.wTiltPos;
info->Z = PTZPOS.wZoomPos;
return;
}
void SetPTZ(LONG lUserID,WORD action,WORD p,WORD t,WORD z){
NET_DVR_PTZPOS PTZPOS;
PTZPOS.wAction = action;
PTZPOS.wPanPos = p;
PTZPOS.wTiltPos = t;
PTZPOS.wZoomPos = z;
NET_DVR_SetDVRConfig(lUserID,292,1,&PTZPOS,sizeof(PTZPOS));
}
void QueryMonth(LONG lUserID,WORD Year,BYTE Month,DWORD Channel,BYTE *byRecordDistribution){
NET_DVR_MRD_SEARCH_PARAM SEARCH_PARAM;
SEARCH_PARAM.dwSize = sizeof(SEARCH_PARAM);
SEARCH_PARAM.wYear = Year;
SEARCH_PARAM.byMonth = Month;
SEARCH_PARAM.struStreamInfo.dwChannel = 32 + Channel;
SEARCH_PARAM.byLocalOrUTC = 1;
SEARCH_PARAM.byDrawFrame = 0;
SEARCH_PARAM.byStreamType = 0;
LPVOID lpStatusList = malloc(sizeof(SEARCH_PARAM));
NET_DVR_MRD_SEARCH_RESULT SEARCH_RESULT;
NET_DVR_GetDeviceConfig(lUserID,6164,0,&SEARCH_PARAM,sizeof(SEARCH_PARAM),lpStatusList,&SEARCH_RESULT,sizeof(SEARCH_RESULT));
free(lpStatusList);
memcpy(byRecordDistribution,SEARCH_RESULT.byRecordDistribution,32);
}
LONG QueryDayHandle(LONG lUserID,DWORD Year,DWORD Month,DWORD Day,LONG Channel){
NET_DVR_FILECOND FindFile;
FindFile.dwFileType = 0xff;
FindFile.dwIsLocked = 0xff;
FindFile.dwUseCardNo = 0;
FindFile.lChannel = 32 + Channel;
FindFile.struStartTime.dwYear = Year;
FindFile.struStartTime.dwMonth = Month;
FindFile.struStartTime.dwDay = Day;
FindFile.struStartTime.dwHour = 0;
FindFile.struStartTime.dwMinute = 0;
FindFile.struStartTime.dwSecond = 0;
FindFile.struStopTime.dwYear = Year;
FindFile.struStopTime.dwMonth = Month;
FindFile.struStopTime.dwDay = Day;
FindFile.struStopTime.dwHour = 23;
FindFile.struStopTime.dwMinute = 59;
FindFile.struStopTime.dwSecond = 59;
return NET_DVR_FindFile_V30(lUserID, &FindFile);
}
LONG QueryDayNextFile(LONG QueryHandle,struct TimeRange *TimeRange){
NET_DVR_FINDDATA_V30 Data;
LONG STATE = NET_DVR_FindNextFile_V30(QueryHandle,&Data);
TimeRange->StartYear = Data.struStartTime.dwYear;
TimeRange->StartMonth = Data.struStartTime.dwMonth;
TimeRange->StartDay = Data.struStartTime.dwDay;
TimeRange->StartHour = Data.struStartTime.dwHour;
TimeRange->StartMinute = Data.struStartTime.dwMinute;
TimeRange->StartSecond = Data.struStartTime.dwSecond;
TimeRange->EndYear = Data.struStopTime.dwYear;
TimeRange->EndMonth = Data.struStopTime.dwMonth;
TimeRange->EndDay = Data.struStopTime.dwDay;
TimeRange->EndHour = Data.struStopTime.dwHour;
TimeRange->EndMinute = Data.struStopTime.dwMinute;
TimeRange->EndSecond = Data.struStopTime.dwSecond;
return STATE;
}
struct SerialData
{
float p;
float t;
float z;
};
struct SerialData serialData;
// 自定义 ntohs 函数(仅适用于小端平台)
uint16_t my_ntohs(uint16_t netshort) {
return (netshort >> 8) | (netshort << 8);
}
void CALLBACK g_fSerialDataCallBack(LONG lSerialHandle, char *pRecvDataBuffer, DWORD dwBufSize, DWORD dwUser)
{
char type = pRecvDataBuffer[3];
printf("type: %x\n", type);
uint16_t raw_value;
memcpy(&raw_value, pRecvDataBuffer + 4, sizeof(uint16_t));
float converted = (float)my_ntohs(raw_value) / 100.0f;
switch (type) {
case 0x59:
serialData.p = converted;
break;
case 0x5b:
serialData.t = converted;
break;
case 0x5d:
serialData.z = converted;
break;
default:
// 未知类型
break;
}
}
void GetPTZPOS(LONG lUserID)
{
char p[7] = {0xff, 0x01, 0x00, 0x51, 0x00, 0x00, 0x52};
char t[7] = {0xff, 0x01, 0x00, 0x53, 0x00, 0x00, 0x54};
char z[7] = {0xff, 0x01, 0x00, 0x55, 0x00, 0x00, 0x56};
LONG lTranHandle = NET_DVR_SerialStart(lUserID, 2,g_fSerialDataCallBack,6);
LONG lSerialChan = 0;
sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, p, 7);
sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, t, 7);
sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, z, 7);
sleep_seconds(1);
NET_DVR_SerialStop(lTranHandle);
}
void SetPTZPOS(LONG lUserID,int action,char* P,char* T,char *Z)
{
LONG lTranHandle = NET_DVR_SerialStart(lUserID, 2,g_fSerialDataCallBack,6);
LONG lSerialChan = 0;
if (action == 1) {
sys_sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, P, 7);
sys_sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, T, 7);
sys_sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, Z, 7);
sys_sleep_seconds(1);
}else if (action == 2) {
sys_sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, P, 7);
sys_sleep_seconds(1);
}else if (action == 3) {
sys_sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, T, 7);
sys_sleep_seconds(1);
}else if (action == 4) {
sys_sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, Z, 7);
sys_sleep_seconds(1);
}else {
sys_sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, P, 7);
sys_sleep_seconds(1);
NET_DVR_SerialSend(lTranHandle, lSerialChan, T, 7);
sys_sleep_seconds(1);
}
NET_DVR_SerialStop(lTranHandle);
sleep(1);
}
*/
import "C"
import (
"math"
"sync"
"unsafe"
)
type LONG int32
type BYTE uint8
type DWORD uint32
type WORD uint16
type NET_DVR_DEVICEINFO struct {
ByChanNum BYTE
ByStartChan BYTE // 起始数字通道号0表示无数字通道比如DVR或IPC
}
// Init 初始化SDK调用其他SDK函数的前提。
func Init() error {
if C.NET_DVR_Init() != 1 {
return lastError("NET_DVR_Init")
}
return nil
}
// Cleanup 释放SDK资源在程序结束之前调用。在调用时不能同时调用其他任何SDK接口。 Init 和 Cleanup 需要配对使用,即程序里面调用多少次 Init ,退出时就需要调用多少次 Cleanup。
func Cleanup() error {
if C.NET_DVR_Cleanup() != 1 {
return lastError("NET_DVR_Cleanup")
}
return nil
}
func login(Ip string, Port int, Username string, Password string) (LONG, NET_DVR_DEVICEINFO, error) {
cIp := C.CString(Ip)
cUser := C.CString(Username)
cPass := C.CString(Password)
defer C.free(unsafe.Pointer(cIp))
defer C.free(unsafe.Pointer(cUser))
defer C.free(unsafe.Pointer(cPass))
var info C.DEVICEINFO
userId := LONG(C.Login(cIp, C.WORD(Port), cUser, cPass, &info))
err := lastError("login")
if err != nil {
return 0, NET_DVR_DEVICEINFO{}, err
}
return userId, NET_DVR_DEVICEINFO{
ByChanNum: BYTE(info.byChanNum),
ByStartChan: BYTE(info.byStartChan)},
nil
}
func lastError(funcName string) error {
ErrorCode := uint(C.NET_DVR_GetLastError())
if ErrorCode == 0 {
return nil
}
var text string
if ErrorCode == 3 {
text = "sdk not init."
return NewHcnetError(int(ErrorCode), text, funcName)
}
cErrorCode := C.LONG(ErrorCode)
cText := C.NET_DVR_GetErrorMsg(&cErrorCode)
text = C.GoString(cText)
return NewHcnetError(int(ErrorCode), text, funcName)
}
func getTimeZone(lUserId LONG) int {
return int(C.GetTimeZone(C.LONG(lUserId)))
}
func deviceOnline(lUserId LONG) bool {
return C.NET_DVR_RemoteControl(C.LONG(lUserId), C.DWORD(20005), nil, C.DWORD(0)) == C.TRUE
}
func logout(lUserId LONG) error {
if lUserId > -1 {
cResult := C.NET_DVR_Logout(C.LONG(lUserId))
if cResult != 1 {
return lastError("NET_DVR_Logout")
}
}
return nil
}
func pTZControlWithSpeed_Other(lUserID LONG, dwPTZCommand DWORD, dwStop DWORD, dwSpeed DWORD) error {
state := C.NET_DVR_PTZControlWithSpeed_Other(C.LONG(lUserID), 1, C.DWORD(dwPTZCommand), C.DWORD(dwStop), C.DWORD(dwSpeed))
if state == C.FALSE {
return lastError("NET_DVR_PTZControlWithSpeed_Other")
}
return nil
}
func getPTZBase(lUserID LONG) (PTZ, error) {
var info C.PTZ
C.GetPTZ(C.LONG(lUserID), &info)
err := lastError("GetPTZ")
if err != nil {
return PTZ{}, err
}
return PTZ{
P: HEX2DEC(WORD(info.P)),
T: HEX2DEC(WORD(info.T)),
Z: HEX2DEC(WORD(info.Z))},
nil
}
func setPTZBase(lUserID LONG, ptz PTZ) error {
C.SetPTZ(C.LONG(lUserID), C.WORD(ptz.Action), C.WORD(DEC2HEX(float64(ptz.P))), C.WORD(DEC2HEX(float64(ptz.T))), C.WORD(DEC2HEX(float64(ptz.Z))))
return lastError("SetPTZ")
}
// HEX2DEC 将十六进制整数转换为十进制浮点数
func HEX2DEC(hex WORD) float64 {
// 提取十六进制数的千位(对应十进制的百位)
bai := byte(hex >> 12)
hex = hex - WORD(bai)*WORD(math.Pow(16, 3))
// 提取十六进制数的百位(对应十进制的十位)
shi := byte(hex >> 8)
hex = hex - WORD(shi)*WORD(math.Pow(16, 2))
// 提取十六进制数的十位(对应十进制的个位)
ge := byte(hex >> 4)
hex = hex - WORD(ge)*WORD(math.Pow(16, 1))
// 提取十六进制数的个位(对应十进制的十分位)
xiao := byte(hex)
// 计算对应的十进制浮点数
return float64(bai)*math.Pow(10, 2) + float64(shi)*math.Pow(10, 1) + float64(ge)*math.Pow(10, 0) + float64(xiao)*math.Pow(10, -1)
}
// DEC2HEX 将十进制浮点数转换为十六进制整数
func DEC2HEX(dec float64) WORD {
// 提取千位(对应十进制的百位)
bai := uint16(dec / math.Pow(10, 2))
dec -= float64(bai) * math.Pow(10, 2)
// 提取百位(对应十进制的十位)
shi := uint16(dec / math.Pow(10, 1))
dec -= float64(shi) * math.Pow(10, 1)
// 提取十位(对应十进制的个位)
ge := uint16(dec / math.Pow(10, 0))
dec -= float64(ge) * math.Pow(10, 0)
// 提取个位(对应十进制的十分位)
xiao := uint16(dec * 10)
// 合成十六进制数
hex := WORD(bai)<<12 | WORD(shi)<<8 | WORD(ge)<<4 | WORD(xiao)
return hex
}
func QueryMonth(lUserID LONG, Year WORD, Month BYTE, Channel DWORD) (res []uint8, err error) {
buf := (*C.BYTE)(C.malloc(32))
defer C.free(unsafe.Pointer(buf))
C.QueryMonth(C.LONG(lUserID), C.WORD(Year), C.BYTE(Month), C.DWORD(Channel), buf)
goBuf := C.GoBytes(unsafe.Pointer(buf), 32)
for i := 0; i < 32; i++ {
if goBuf[i] != 0 {
res = append(res, uint8(i+1))
}
}
err = lastError("QueryMonth")
return
}
func QueryDayHandle(lUserID LONG, Year, Month, Day DWORD, Channel LONG) LONG {
return LONG(C.QueryDayHandle(C.LONG(lUserID), C.DWORD(Year), C.DWORD(Month), C.DWORD(Day), C.LONG(Channel)))
}
func QueryDayNextFile(Handle LONG) (TimeRange, LONG, error) {
var FindData C.TimeRange
state := LONG(C.QueryDayNextFile(C.LONG(Handle), &FindData))
if state < 0 {
return TimeRange{}, state, lastError("QueryDayNextFile")
}
return TimeRange{
StartYear: DWORD(FindData.StartYear),
StartMonth: DWORD(FindData.StartMonth),
StartDay: DWORD(FindData.StartDay),
StartHour: DWORD(FindData.StartHour),
StartMinute: DWORD(FindData.StartMinute),
StartSecond: DWORD(FindData.StartSecond),
EndYear: DWORD(FindData.EndYear),
EndMonth: DWORD(FindData.EndMonth),
EndDay: DWORD(FindData.EndDay),
EndHour: DWORD(FindData.EndHour),
EndMinute: DWORD(FindData.EndMinute),
EndSecond: DWORD(FindData.EndSecond),
}, state, nil
}
func FindClose(Handle LONG) error {
C.NET_DVR_FindClose_V30(C.LONG(Handle))
return lastError("NET_DVR_FindClose_V30")
}
var SerialMux sync.Mutex
func SetPTZPos(lUserID LONG, Action int, P []byte, T []byte, Z []byte) error {
cP := C.CBytes(P)
cT := C.CBytes(T)
cZ := C.CBytes(Z)
defer C.free(cP)
defer C.free(cT)
defer C.free(cZ)
SerialMux.Lock()
C.SetPTZPOS(C.LONG(lUserID), C.int(Action), (*C.char)(cP), (*C.char)(cT), (*C.char)(cZ))
SerialMux.Unlock()
return lastError("SetPTZPOS")
}
func GetPTZPOS(lUserID LONG) (PTZ, error) {
SerialMux.Lock()
C.GetPTZPOS(C.LONG(lUserID))
p := float32(C.serialData.p)
t := float32(C.serialData.t)
z := float32(C.serialData.z)
SerialMux.Unlock()
return PTZ{
P: float64(p),
T: float64(t),
Z: float64(z),
}, lastError("GetPTZPOS")
}