Bootstrap

CGO登录海康设备并使用Go1.17 unsafe包新特性

本文通过一个常用的登录设备的例子,演示学习go1.17 unsafe.Slice方法的使用。具体例子如下:

package main

/*
#cgo CFLAGS: -I./libs/hiksdk-6.1.4.51/include/
#cgo LDFLAGS: -L./libs/hiksdk-6.1.4.51/lib/ -lHCCore -lhcnetsdk

#include "HCNetSDK.h"
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
*/
import "C"
import (
	"fmt"
	"reflect"
	"unsafe"
)

type NET_DVR_LOCAL_SDK_PATH struct {
	Path [256]byte
	Res  [128]byte
}

func main() {
	ret := C.NET_DVR_Init()
	if int(ret) != 1 {
		fmt.Printf("NET_DVR_Init failed,error code = %v\n", C.NET_DVR_GetLastError())
		return
	}
	defer C.NET_DVR_Cleanup()

	// 开启调试日志,不开启可以注掉,sdk的日志能看到详细的错误信息
	logPath := "/tmp"
	C.NET_DVR_SetLogToFile(3, C.CString(logPath), 1)

	// 如果动态库加载失败,可调用SDK手动设置海康动态库的路径,不设置可以注掉,如果加载不成功,尝试显示设置下
	var comPath = NET_DVR_LOCAL_SDK_PATH{}
	copy(comPath.Path[:], "/root/script/src/nvr/libs/hiksdk-6.1.4.51/lib/")
	C.NET_DVR_SetSDKInitCfg(C.NET_SDK_INIT_CFG_SDK_PATH, unsafe.Pointer(&comPath))

	var userLoginInfo C.NET_DVR_USER_LOGIN_INFO
	var deviceInfo C.NET_DVR_DEVICEINFO_V40

	userLoginInfo.wPort = 8000 // your device port,default 8000

	username := "your device username"
	// 转换成C字节数组
	pUsername := C.CBytes([]byte(username))
	defer C.free(pUsername)
	// 使用memcpy函数进行拷贝
	C.memcpy(unsafe.Pointer(&userLoginInfo.sUserName), pUsername, C.ulong(len(username)))

	password := "you device password"
	pPassword := C.CBytes([]byte(password))
	defer C.free(pPassword)
	C.memcpy(unsafe.Pointer(&userLoginInfo.sPassword), pPassword, C.ulong(len(password)))

	deviceIp := "your device ip"
	pDeviceIp := C.CBytes([]byte(deviceIp))
	defer C.free(pDeviceIp)
	C.memcpy(unsafe.Pointer(&userLoginInfo.sDeviceAddress), pDeviceIp, C.ulong(len(deviceIp)))

	// 调用登录接口
	loginId := C.NET_DVR_Login_V40((C.LPNET_DVR_USER_LOGIN_INFO)(&userLoginInfo), (C.LPNET_DVR_DEVICEINFO_V40)(&deviceInfo))
	if int32(loginId) < 0 {
		fmt.Printf("NET_DVR_Login_V40 failed,error code = %v", C.NET_DVR_GetLastError())
		return
	}

	byIPChanNum := uint8(deviceInfo.struDeviceV30.byIPChanNum)
	byHighDChanNum := uint8(deviceInfo.struDeviceV30.byHighDChanNum)

	// go1.17 unsafe.Slice 从给定的地址和长度产生一个任意类型的切片,须保证在使用中地址不能被释放
	// 下面参数中的地址未C内存结构的数字第一个元素的地址
	serialNo := unsafe.Slice((*byte)(unsafe.Pointer(&deviceInfo.struDeviceV30.sSerialNumber[0])), int(C.SERIALNO_LEN))

	fmt.Printf("用户ID:%d\n", int32(loginId))
	fmt.Printf("序列号:%s\n", string(serialNo))
	fmt.Printf("硬盘个数:%d\n", uint8(deviceInfo.struDeviceV30.byDiskNum))
	fmt.Printf("设备类型:%d\n", uint8(deviceInfo.struDeviceV30.byDVRType))
	fmt.Printf("设备模拟通道个数:%d\n", uint8(deviceInfo.struDeviceV30.byChanNum))
	fmt.Printf("设备模拟通道起始通道号:%d\n", uint8(deviceInfo.struDeviceV30.byStartChan))
	fmt.Printf("设备数字通道起始通道号:%d\n", uint8(deviceInfo.struDeviceV30.byStartDChan))
	fmt.Printf("设备IP通道个数:%d\n", uint16(byIPChanNum)+uint16(byHighDChanNum)*256)
}

运行结果如下:

用户ID:0
序列号:DS-删掉后面若干
硬盘个数:2
设备类型:90
设备模拟通道个数:0
设备模拟通道起始通道号:1
设备数字通道起始通道号:33
设备IP通道个数:16

;