很早就开始用cython方法,ctype方法,最近知道还有个pythonnet方法。分别说来。
根目录先放inc和lib ,分别是厂商的.h文件和.so文件。然后根据头文件写cython的声明文件wrap.pyd:
# -*- coding: UTF-8 -*-
# -*- coding: UTF-8 -*-
# cdef extern from "string.h":
# void* memcpy(void* dest, void* src,size_t count) except +
cdef extern from "HCNetSDK.h":
ctypedef int BOOL
ctypedef unsigned int DWORD
ctypedef unsigned short WORD
ctypedef unsigned short USHORT
ctypedef short SHORT
ctypedef int LONG
ctypedef unsigned char BYTE
ctypedef unsigned int UINT
ctypedef void*LPVOID
ctypedef void*HANDLE
ctypedef unsigned int*LPDWORD
ctypedef unsigned long long UINT64
ctypedef signed long long INT64
cdef enum:
SERIALNO_LEN=48
NAME_LEN=32
MACADDR_LEN=6
COMM_ALARM=0x1100 #8000报警信息主动上传
MAX_DISKNUM=16#8000设备最大硬盘数
MAX_CHANNUM=16#8000设备最大通道数
MAX_ALARMOUT=4#8000设备最大报警输出数
ctypedef struct NET_DVR_DEVICEINFO_V30:
BYTE sSerialNumber[SERIALNO_LEN]
BYTE byAlarmInPortNum
BYTE byAlarmOutPortNum
BYTE byDiskNum
BYTE byDVRType
BYTE byChanNum
BYTE byStartChan
BYTE byAudioChanNum
BYTE byIPChanNum
BYTE byZeroChanNum
BYTE byMainProto
BYTE bySubProto
BYTE bySupport
BYTE bySupport1
BYTE bySupport2
WORD wDevType
BYTE bySupport3
BYTE byMultiStreamProto
BYTE byStartDChan
BYTE byStartDTalkChan
BYTE byHighDChanNum
BYTE bySupport4
BYTE byLanguageType
BYTE byVoiceInChanNum
BYTE byStartVoiceInChanNo
BYTE bySupport5
BYTE bySupport6
BYTE byMirrorChanNum
WORD wStartMirrorChanNo
BYTE bySupport7
BYTE byRes2
ctypedef struct NET_DVR_SDKSTATE:
DWORD dwTotalLoginNum
DWORD dwTotalRealPlayNum
DWORD dwTotalPlayBackNum
DWORD dwTotalAlarmChanNum
DWORD dwTotalFormatNum
DWORD dwTotalFileSearchNum
DWORD dwTotalLogSearchNum
DWORD dwTotalSerialNum
DWORD dwTotalUpgradeNum
DWORD dwTotalVoiceComNum
DWORD dwTotalBroadCastNum
DWORD dwTotalListenNum
DWORD dwEmailTestNum
DWORD dwBackupNum
DWORD dwTotalInquestUploadNum
DWORD dwRes[6]
ctypedef NET_DVR_DEVICEINFO_V30 *LPNET_DVR_DEVICEINFO_V30
ctypedef NET_DVR_SDKSTATE *LPNET_DVR_SDKSTATE
BOOL __stdcall NET_DVR_Init() except +
BOOL __stdcall NET_DVR_Cleanup() except +
BOOL __stdcall NET_DVR_SetConnectTime(DWORD dwWaitTime, DWORD dwTryTime) except +
BOOL __stdcall NET_DVR_SetReconnect(DWORD dwInterval, BOOL bEnableRecon) except +
DWORD __stdcall NET_DVR_GetLastError() except +
LONG __stdcall NET_DVR_Login_V30(char *sDVRIP, WORD wDVRPort, char *sUserName, char *sPassword, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo) except +
BOOL __stdcall NET_DVR_Logout_V30(LONG lUserID) except +
BOOL __stdcall NET_DVR_Logout(LONG lUserID) except +
BOOL NET_DVR_GetSDKState( LPNET_DVR_SDKSTATE pSDKState) except +
ctypedef struct NET_DVR_ALARMER:
BYTE byUserIDValid# userid是否有效 0-无效,1-有效 */
BYTE bySerialValid# 序列号是否有效 0-无效,1-有效 */
BYTE byVersionValid# 版本号是否有效 0-无效,1-有效 */
BYTE byDeviceNameValid# 设备名字是否有效 0-无效,1-有效 */
BYTE byMacAddrValid# MAC地址是否有效 0-无效,1-有效 */
BYTE byLinkPortValid# login端口是否有效 0-无效,1-有效 */
BYTE byDeviceIPValid# 设备IP是否有效 0-无效,1-有效 */
BYTE bySocketIPValid# socket ip是否有效 0-无效,1-有效 */
LONG lUserID# NET_DVR_Login()返回值, 布防时有效 */
BYTE sSerialNumber[SERIALNO_LEN]# /* 序列号 */
DWORD dwDeviceVersion# /* 版本信息 高16位表示主版本,低16位表示次版本*/
char sDeviceName[NAME_LEN]# /* 设备名字 */
BYTE byMacAddr[MACADDR_LEN]# /* MAC地址 */
WORD wLinkPort# link port */
char sDeviceIP[128]# /* IP地址 */
char sSocketIP[128]# /* 报警主动上传时的socket IP地址 */
BYTE byIpProtocol# /* Ip协议 0-IPV4, 1-IPV6 */
BYTE byRes1[2]
BYTE bJSONBroken# //JSON断网续传标志。0:不续传;1:续传
WORD wSocketPort
BYTE byRes2[6]
ctypedef NET_DVR_ALARMER *LPNET_DVR_ALARMER
ctypedef struct NET_DVR_ALARMINFO:
DWORD dwAlarmType#/*0-信号量报警,1-硬盘满,2-信号丢失,3-移动侦测,4-硬盘未格式化,5-读写硬盘出错,6-遮挡报警,7-制式不匹配, 8-非法访问, 9-视频信号异常,10-录像异常 11- 智能场景变化*/
DWORD dwAlarmInputNumber#/*报警输入端口*/
DWORD dwAlarmOutputNumber[MAX_ALARMOUT]#/*触发的输出端口,哪一位为1表示对应哪一个输出*/
DWORD dwAlarmRelateChannel[MAX_CHANNUM]#/*触发的录像通道,哪一位为1表示对应哪一路录像, dwAlarmRelateChannel[0]对应第1个通道*/
DWORD dwChannel[MAX_CHANNUM]#/*dwAlarmType为2或3,6,9,10时,表示哪个通道,dwChannel[0]位对应第1个通道*/
DWORD dwDiskNumber[MAX_DISKNUM]#/*dwAlarmType为1,4,5时,表示哪个硬盘, dwDiskNumber[0]位对应第1个硬盘*/
ctypedef NET_DVR_ALARMINFO *LPNET_DVR_ALARMINFO
ctypedef void (__stdcall *MSGCallBack)(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser)
BOOL __stdcall NET_DVR_SetDVRMessageCallBack_V30(MSGCallBack fMessageCallBack, void* pUser)
LONG __stdcall NET_DVR_StartListen_V30(char *sLocalIP, WORD wLocalPort, MSGCallBack DataCallback, void* pUserData)
BOOL __stdcall NET_DVR_StopListen_V30(LONG lListenHandle)
自己设计到底用啥,写桥梁过度文件wrap.pyx:
cimport wrap
from libc.stdlib cimport malloc, free
from libc.string cimport memcpy
from cpython.pycapsule cimport *
# cdef object callbackfunc
# cdef void __stdcall MessageCallback(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser):
# cdef wrap.NET_DVR_ALARMINFO struAlarmInfo
# memcpy(&struAlarmInfo, pAlarmInfo, sizeof(wrap.NET_DVR_ALARMINFO))
# altype=struAlarmInfo.dwAlarmType
# chan=-1
# for i in range(0,17):
# if struAlarmInfo.dwChannel[i] == 1:
# chan=i+1
# break
# # 使用request 发送到sever
# global callbackfunc
# (
# return
cdef class Hc:
cdef wrap.NET_DVR_DEVICEINFO_V30* struDeviceInfo
cdef object callbackfunc # 这里不写,下面__init__要报错
def __cinit__(self):
self.struDeviceInfo = <wrap.NET_DVR_DEVICEINFO_V30*>malloc(sizeof(wrap.NET_DVR_DEVICEINFO_V30))
if not self.struDeviceInfo:
raise MemoryError
def __dealloc__(self):
free(self.struDeviceInfo)
def notify(self,success,what):
if success==1:
print (what+"success")
else:print (what+"fail")
return
def __init__(self):
self.callbackfunc=""
def dvr_init(self):
success=wrap.NET_DVR_Init()
self.notify(success,"init")
return
def set_con_time(self):
success=wrap.NET_DVR_SetConnectTime(2000, 1)
self.notify(success,"set con time")
return
def set_recon_time(self):
success=wrap.NET_DVR_SetReconnect(10000, 1)
self.notify(success,"reset time")
return
def get_error(self):
error_code=wrap.NET_DVR_GetLastError()
return error_code
def login_v30(self,ip,port,user,pwd):
userid=wrap.NET_DVR_Login_V30(ip,port,user,pwd,self.struDeviceInfo)
if userid<0:
error_code=self.get_error()
print ("login fail,error code:",error_code)
self.cleanup()
print (self.struDeviceInfo.sSerialNumber)
return userid
def logout_v30(self,userid):
success=wrap.NET_DVR_Logout_V30(userid)
self.notify(success,"logout")
return
def logout(self,userid):
success=wrap.NET_DVR_Logout(userid)
self.notify(success,"logout")
return
# 函数内部分配内存,free
cpdef get_version(self):
cdef wrap.NET_DVR_SDKSTATE* psdkstate
psdkstate=<wrap.NET_DVR_SDKSTATE*>malloc(sizeof(wrap.NET_DVR_SDKSTATE))
if not psdkstate:
raise MemoryError
success=wrap.NET_DVR_GetSDKState(psdkstate)
self.notify(success,"get version")
print (psdkstate.dwTotalLoginNum)
free(psdkstate)
return
def cleanup(self):
success=wrap.NET_DVR_Cleanup()
self.notify(success,"clean up")
return
def stop_listen(self,handler):
status=wrap.NET_DVR_StopListen_V30(handler)
return status
# 在set callback中把pyfunc创建来,让他等于self。callback,真正的c callback就是下面的中,运行pyfunc,达到运行时传入函数控制的目的
cdef void __stdcall MessageCallback(self,LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser):
cdef wrap.NET_DVR_ALARMINFO struAlarmInfo
memcpy(&struAlarmInfo, pAlarmInfo, sizeof(wrap.NET_DVR_ALARMINFO))
altype=struAlarmInfo.dwAlarmType
chan=-1
for i in range(0,17):
if struAlarmInfo.dwChannel[i] == 1:
chan=i+1
break
# 使用request 发送到sever
(<object>self.callbackfunc)(lCommand,altype,chan)
return
cpdef set_callback_listen(self,pyfunc,port,lUserID):
# global callbackfunc
self.callbackfunc=pyfunc
wrap.NET_DVR_SetDVRMessageCallBack_V30(<wrap.MSGCallBack>self.MessageCallback, NULL)
lHandle = wrap.NET_DVR_StartListen_V30(NULL,port, <wrap.MSGCallBack>self.MessageCallback, NULL)
if lHandle< 0:
print ("NET_DVR_StartListen_V30 error code", self.get_error())
self.logout(lUserID)
self.cleanup()
return
setup.py文件 python setup.py build_ext -i
# -*- coding: utf-8 -*-
from distutils.core import setup, Extension
from Cython.Build import cythonize
import os
hccom = list(map(lambda x: x[3:-3], os.listdir("./lib/HCNetSDKCom")))
libs=["hcnetsdk", "HCCore", "ssl", "crypto", ":libcrypto.so.1.0.0"] + hccom
print ("*********************************************************************")
print(libs)
setup(
ext_modules=cythonize(
Extension(
'wrap',#mod name should be equal pyx file name,so the file will be entrance
sources=['wrap.pyx'],
language="c++",
include_dirs=[os.path.abspath("./inc")],
library_dirs=[os.path.abspath('.'), os.path.abspath("./lib"),os.path.abspath("./lib/HCNetSDKCom")],
libraries=libs,
extra_compile_args=[],
extra_link_args=[]
),
compiler_directives={'language_level': 3},
)
)
# build_ext --inplace
# sources 里面可以包含 .pyx 文件,以及后面如果我们要调用 C/C++ 程序的话,还可以往里面加 .c / .cpp 文件
# language 其实默认就是 c,如果要用 C++,就改成 c++ 就好了
# include_dirs 这个就是传给 gcc 的 -I 参数
# library_dirs 这个就是传给 gcc 的 -L 参数
# libraries 这个就是传给 gcc 的 -l 参数
# extra_compile_args 就是传给 gcc 的额外的编译参数,比方说你可以传一个 -std=c++11
# extra_link_args 就是传给 gcc 的额外的链接参数(也就是生成动态链接库的时候用的)
# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib
# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib/HCNetSDKCom
# etc 下有一个ld的conf可以增加 ldconfig
最后是调用的例子
# -*- coding: utf-8 -*-
import os,sys
import wrap
alarms={0:"信号量报警",
1:"硬盘满",
2:'信号丢失',
3:'移动侦测',
4:'硬盘未格式化',
5:'读写硬盘出错',
6:'遮挡报警',
7:'制式不匹配',
8:'非法访问',
9:'视频信号异常',
10:'录像异常',
11: '智能场景变化'}
def pyfunc(lCommand,altype,chan):
print ("消息类型",lCommand)
print ("alarm type",altype,alarms.get(altype,"not found"))
print ("channel number",chan)
return
hc=wrap.Hc()
hc.dvr_init()
hc.set_con_time()
hc.set_recon_time()
userid=hc.login_v30(b"192.168.2.102", 8000, b"admin", b"wangqi1977")
print ("userid",userid)
hc.get_version()
port=8087
hc.set_callback_listen(pyfunc,port,userid)
hc.logout(userid)
hc.cleanup()
这个简单很多,不过cython的好处在于可以自己设计api,把c的回调函数什么的调用设计更加pythonic
from ctypes import*
dll='./TSCLIB.dll'
TSCLIB_DLL= cdll.LoadLibrary(dll)
TSCLIB_DLL.openport("TSC TTP-245C")
# specified printer driver
TSCLIB_DLL.setup("100", "63.5", "4", "8", "0", "0", "0")
TSCLIB_DLL.clearbuffer()
TSCLIB_DLL.barcode("100", "100", "128", "100", "1", "0", "2", "2", "Barcode Test")
TSCLIB_DLL.printerfont("100", "250", "3", "0", "1", "1", "Print Font Test")
TSCLIB_DLL.windowsfont(100, 300, 24, 0, 0, 0, "ARIAL", "Windows Arial Font Test")
TSCLIB_DLL.downloadpcx("UL.PCX", "UL.PCX")
TSCLIB_DLL.sendcommand("PUTPCX 100,400,\"UL.PCX\"")
TSCLIB_DLL.printlabel("1", "1")
TSCLIB_DLL.closeport()
这个是有条件的,就是dll符合com接口,比如c#代码编译的出ClassLibrary1.dll:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace dllTest
{
public class Operate
{
public int GetSum(int a, int b)
{
return a + b;
}
}
}
py调用
import clr # pythonnet
# clr.FindAssembly('ClassLibrary1.dll')
clr.AddReference('ClassLibrary1')
from dllTest import Operate
s=Operate()
a=s.GetSum(12,3)
print (a)