嗨,我需要从 c3-400 设备获取实时事件,我使用了 zkpull sdk 文档中描述的 GetRTlog 方法,但它总是返回门/警报状态并且从不返回实时事件。我使用此代码与使用 tcp/ip 配置了 192.168.52.249 ip 地址的 c3-400 访问设备进行通信,只有当设备中没有实时事件时它才应该返回门/警报状态,但它虽然有是设备上的事件我在这里做错了什么,任何人都可以帮忙
要重现这个,你需要一个 c3-400 设备,带有 zkpull sdk dll 文件的 windows 机器
zk = ZK('192.168.52.249')
zk.connect()
n=5
while n > 0:
events = zk.read_events()
for e in events:
res='{}"code":[{}"event_type":"{}","door":"{}","card":"{}","pin":"{}","time":"{}","entry_exit":"{}","verify_mode":"{}"{}]{}'.format("{","{",e.event_type,e.door,e.card,e.pin,"",e.entry_exit,e.verify_mode,"}","}")
print('res',res)
n -= 1
time.sleep(1)
zk.disconnect()
ZK 码是
import ctypes
from .ZKEnum import *
class ZKRealtimeEvent:
"""
Represents one realtime event occured on the device
Since the device returns event as string we need to parse it to the structured view. This class does this.
"""
__slots__ = (
'time',
'pin',
'card',
'door',
'event_type',
'entry_exit',
'verify_mode'
)
def __init__(self, s=None):
"""
:param s: Optional. Event string to be parsed.
"""
if s:
self.parse(s)
def parse(self, s):
"""
Parse one event string and fills out slots
:param s: event string
:raises ValueError: event string is invalid
:return:
"""
if s == '' or s == '\r\n':
raise ValueError("Empty event string")
items = s.split(',')
if len(items) != 7:
raise ValueError("Event string has not 7 comma-separated parts")
items[0] = datetime.strptime(items[0], '%Y-%m-%d %H:%M:%S')
#items[0] = items[0]
for i in range(len(self.__slots__)):
setattr(self, self.__slots__[i], items[i])
class ZK():
@property
def device_model(self):
"""Device model class. Read only"""
return self._device_model
def __init__(self,ip='192.168.52.249',device_model=ZK400):
self.ip=ip
self.connect_params='protocol=TCP,ipaddress={},port=4370,timeout=4000,passwd='.format(ip)
self.handle=False
self._device_model=device_model
# self.session_id = 0
# self.reply_number = 0
# self.connected_flg = False
def __str__(self):
return 'ZK access class'
def connect(self):
if(self.handle):
raise RuntimeError('Already connected')
self.connect_buffer=ctypes.create_string_buffer(self.connect_params.encode('utf-8'))
self.commpro=ctypes.windll.LoadLibrary("plcommpro.dll")
self.handle=self.commpro.Connect(self.connect_buffer)
if self.handle ==0:
raise RuntimeError('Coud not connect to '+self.ip)
def disconnect(self):
"""
Disconnect from device
:return:
"""
if not self.handle:
return
ret=self.commpro.Disconnect(self.handle)
if ret ==0:
raise RuntimeError('Coud not disconnect from '+self.ip)
self.handle = False
def getDeviceParam(self ,params=False):
if not params:
params="DeviceID,LockCount,GATEIPAddress,NetMask"
my_buffer=ctypes.create_string_buffer(10*1024*1024)
items = bytes(params,'utf-8')
p_items = ctypes.create_string_buffer(items)
ret = self.commpro.GetDeviceParam(self.handle,my_buffer,256,p_items)
if ERRORS.get(ret):
raise RuntimeError(ERRORS.get(ret)+' '+self.ip)
rett=my_buffer.value.decode('utf-8')
result=rett.split('\r\n')
result=result[0]
result_arr=result.split(',')
result_dic={}
for r in result_arr:
x=r.split('=')
result_dic[x[0]]=x[1]
return result_dic
def setDeviceParam(self ,params=False):
if not params:
raise RuntimeError('setDeviceParam params is missing')
items = bytes(params,'utf-8')
p_items = ctypes.create_string_buffer(items)
ret = self.commpro.SetDeviceParam(self.handle,p_items)
if ERRORS.get(ret):
raise RuntimeError(ERRORS.get(ret)+' '+self.ip)
return ret
def editCardZone(self, zone_id,card):
tablev=b"userauthorize"
datdav="Pin="+str(card.pk)+"\tAuthorizeTimezoneId="+str(zone_id)
datav=bytes(datdav,'utf-8')
p_tablev=ctypes.create_string_buffer(tablev)
str_bufv=ctypes.create_string_buffer(datav)
res=self.commpro.SetDeviceData(self.handle, p_tablev, str_bufv, '')
if ERRORS.get(res):
raise RuntimeError(ERRORS.get(res)+' '+self.ip)
return res
def addCard(self, pin,cardno,password,zone=1,doors=15):
pin=str(pin)
cardno=str(cardno)
password=str(password)
table=b"user"
datda="Pin="+pin+"\tCardNo="+cardno+"\tPassword="+password
data=bytes(datda,'utf-8')
tablev=b"userauthorize"
datdav="Pin="+pin+"\tAuthorizeTimezoneId="+str(zone)+"\tAuthorizeDoorId="+str(doors)
datav=bytes(datdav,'utf-8')
p_tablev=ctypes.create_string_buffer(tablev)
str_bufv=ctypes.create_string_buffer(datav)
self.commpro.SetDeviceData(self.handle, p_tablev, str_bufv, '')
p_table=ctypes.create_string_buffer(table)
str_buf=ctypes.create_string_buffer(data)
res=self.commpro.SetDeviceData(self.handle,p_table,str_buf,'')
if ERRORS.get(res):
raise RuntimeError(ERRORS.get(res)+' '+self.ip)
return res
def getCard(self):
table = b"USER" # Download the user data from the user table
fieldname = b"*" # Download all field information in the table
pfilter = b"" # Have no filtering conditions and thus download all information
options = b""
query_buf = ctypes.create_string_buffer(4*1024*1024)
query_table = ctypes.create_string_buffer(table)
query_fieldname = ctypes.create_string_buffer(fieldname)
query_filter = ctypes.create_string_buffer(pfilter)
query_options = ctypes.create_string_buffer(options)
ret = self.commpro.GetDeviceData(self.handle, query_buf, 4*1024*1024, query_table, query_fieldname, query_filter, query_options)
if ERRORS.get(ret):
raise RuntimeError(ERRORS.get(ret)+' '+self.ip)
rett=query_buf.value.decode('utf-8')
return rett.split('\r\n')
def removeCard(self,pin):
table=b"user"
pin=str(pin)
data=bytes("Pin="+pin, 'utf-8')
p_table=ctypes.create_string_buffer(table)
p_data=ctypes.create_string_buffer(data)
res=self.commpro.DeleteDeviceData(self.handle,p_table,p_data,'')
if ERRORS.get(res):
raise RuntimeError(ERRORS.get(res)+' '+self.ip)
return res
def addTimeZone(self,datda):
table=b"timezone"
data=bytes(datda,'utf-8')
p_table=ctypes.create_string_buffer(table)
str_buf=ctypes.create_string_buffer(data)
res=self.commpro.SetDeviceData(self.handle,p_table,str_buf,'')
if ERRORS.get(res):
raise RuntimeError(ERRORS.get(res)+' '+self.ip)
return res
def getTimeZones(self):
table = b"timezone" # Download the user data from the user table
fieldname = b"*" # Download all field information in the table
pfilter = b"" # Have no filtering conditions and thus download all information
options = b""
query_buf = ctypes.create_string_buffer(4*1024*1024)
query_table = ctypes.create_string_buffer(table)
query_fieldname = ctypes.create_string_buffer(fieldname)
query_filter = ctypes.create_string_buffer(pfilter)
query_options = ctypes.create_string_buffer(options)
ret = self.commpro.GetDeviceData(self.handle, query_buf, 4*1024*1024, query_table, query_fieldname, query_filter, query_options)
if ERRORS.get(res):
raise RuntimeError(ERRORS.get(res)+' '+self.ip)
rett=query_buf.value.decode('utf-8')
result=rett.split('\r\n')
result1=result[0]
result2=result[1]
result_arr1=result1.split(',')
result_arr2=result2.split(',')
result_dic={}
zones_ids=[]
for x in range(len(result)-2):
result_dic={}
arr=(result[x+1]).split(',')
zones_ids.append(arr[0])
# for num , param in enumerate(result_arr1):
# result_dic[param]=result_arr2[num]
# for num , param in enumerate(result_arr1):
# result_dic[param]=result_arr2[num]
return zones_ids
def editTimeZone(self, edit_str):
tablev=b"timezone"
datav=bytes(edit_str,'utf-8')
p_tablev=ctypes.create_string_buffer(tablev)
str_bufv=ctypes.create_string_buffer(datav)
res=self.commpro.SetDeviceData(self.handle, p_tablev, str_bufv, '')
if ERRORS.get(res):
raise RuntimeError(ERRORS.get(res)+' '+self.ip)
return res
def removeTimeZone(self,zone_id):
table=b"timezone"
timeId=str(zone_id)
data=bytes("TimezoneId="+timeId, 'utf-8')
p_table=ctypes.create_string_buffer(table)
p_data=ctypes.create_string_buffer(data)
res=self.commpro.DeleteDeviceData(self.handle,p_table,p_data,'')
if ERRORS.get(res):
raise RuntimeError(ERRORS.get(res)+' '+self.ip)
return data
def getAccessLogs(self):
formmated_events=[]
events=self.transaction()
events.pop(0)
events = events[:-1]
elem=None
for event in events:
elem=event.split(',')
if elem[0]!='0':
date=formatTime(elem[6])
obj={
'card_no':elem[0],
'pin':elem[1],
'verified':VERIFY_MODES.get(elem[2]),
'door_id':elem[3],
'event_type':EVENT_TYPES.get(elem[4]),
'in_out_state':elem[5],
'time_stamp':date
}
formmated_events.append(obj)
return formmated_events
def transaction(self):
table = b"transaction" # Download the user data from the user table
fieldname = b"*" # Download all field information in the table
pfilter = b"" # Have no filtering conditions and thus download all information
options = b""
query_buf = ctypes.create_string_buffer(4*1024*1024)
query_table = ctypes.create_string_buffer(table)
query_fieldname = ctypes.create_string_buffer(fieldname)
query_filter = ctypes.create_string_buffer(pfilter)
query_options = ctypes.create_string_buffer(options)
ret = self.commpro.GetDeviceData(self.handle, query_buf, 4*1024*1024, query_table, query_fieldname, query_filter, query_options)
if ERRORS.get(ret):
raise RuntimeError(ERRORS.get(ret)+' '+self.ip)
rett=query_buf.value.decode('utf-8')
rett=rett.split('\r\n')
return rett
def getDeviceDataCount(self):
table=ctypes.create_string_buffer(b"user")
table_filter=ctypes.create_string_buffer(b"")
options=ctypes.create_string_buffer(b"")
ret = self.commpro.GetDeviceData(self.handle,table,table_filter,options)
print(ret)
def deleteDeviceData(self):
pass
def searchDevice(self):
pass
def modifyIPAddress(self):
pass
def openDoor(self,num,group=1):
self.enable_relay(group,num)
def getRTLog(self, buffer_size=4096):
"""
Machinery method for retrieving events from device.
:param buffer_size: Required. Buffer size in bytes that filled with contents
:raises RuntimeError: if operation was failed
:return: raw string with events
"""
buf = ctypes.create_string_buffer(buffer_size)
res = self.commpro.GetRTLog(self.handle, buf, buffer_size)
if ERRORS.get(res):
raise RuntimeError(ERRORS.get(ret)+' '+self.ip)
ret=buf.value.decode('utf-8')
print('rrrrrrrrrrrrr',ret)
return ret
def read_events(self, buffer_size=4096):
"""
Read events from the device
:param buffer_size:
:return:
"""
raw = self.getRTLog(buffer_size)
*events_s, empty = raw.split('\r\n')
return (ZKRealtimeEvent(s) for s in events_s)
def enable_relay(self, group=1, number=2, timeout=10):
"""
Enable specified relay for the given time. Already enabled relay keep its state, but timeout overwrites with new
value.
:param group: Relay group, see RelayGroup enum. Number between 1 and 4
:param number: Relay number in specified group
:param timeout: Seconds the relay will be enabled. Number between 0 and 255
:raises ValueError: invalid parameter
:raises RuntimeError: operation failed
:return:
"""
if number < 1 or number > self.device_model.relays:
raise ValueError("Incorrect relay number: {}".format(number))
if timeout < 0 or timeout > 255:
raise ValueError("Incorrect timeout: {}".format(timeout))
self.controlDevice(
ControlOperation.output,
number,
group,
timeout,
0
)
def controlDevice(self, operation, p1, p2, p3, p4, options_str=''):
"""
Device control machinery method. Read PULL SDK docs for parameters meaning
:param operation: Number, operation id
:param p1: Number, depends on operation id
:param p2: Number, depends on operation id
:param p3: Number, depends on operation id
:param p4: Number, depends on operation id
:param options_str: String, depends on operation id
:raises RuntimeError: if operation was failed
:return: dll function result code, 0 or positive number
"""
res = self.commpro.ControlDevice(
self.handle,
operation,
p1,
p2,
p3,
p4,
options_str
)
if res < 0:
raise RuntimeError('ControlDevice failed, params: ({}), returned: {}'.format(
','.join((str(self.handle), str(operation), str(p1), str(p2), str(p3), str(p4), str(options_str))),
str(res)
))
return res
和枚举代码是
class ControlOperation:
"""
Type of device control operation. See PULL SDK docs
"""
output = 1
cancel_alarm = 2
restart = 3
class RelayGroup:
"""
Device relay group. See PULL SDK docs
"""
lock = 1
aux = 2
VERIFY_MODES = {
'1': 'Only finger',
'3': 'Only password',
'4': 'Only card',
'11': 'Card and password',
'200': 'Others'
}
ERRORS = {
'-1': 'The command is not sent successfully',
'-2': 'The command has no response',
'-3': 'The buffer is not enough',
'-4': 'The decompression fails',
'-5': 'The length of the read data is not correct ',
'-6': 'The length of the decompressed data is not consistent with expected',
'-7': 'The command is repeated ',
'-8': 'The connection is not authorized ',
'-9': 'Data error: The CRC result is failure',
'-10': 'Data error: PullSDK cannot resolve the data ',
'-11': 'Data parameter error',
'-12': 'The command is not executed correctly',
'-13': 'Command error: This command is not available ',
'-14': 'The communication password is not correct',
'-15': 'Fail to write the file',
'-16': 'Fail to read the file',
'-17': 'The file does not exist',
'-99': 'Uknown error',
'-100': 'The table structure does not exist ',
'-101': 'In the table structure, In the table structure, In the table structure',
'-102': 'The total number of fields is not consistent ',
'-103': 'The sequence of fields is not consistent ',
'-104': 'Real-time event data error ',
'-105': 'Data errors occur during data resolution. ',
'-106': 'Data overflow: The delivered data is more than 4 MB in length ',
'-107': 'Fail to get the table structure ',
'-108': 'Invalid options',
'-201': 'LoadLibrary failure ',
'-202': 'Fail to invoke the interface ',
'-203': 'Communication initialization fails ',
'-206': 'Start of a serial interface agent program fails and the cause generally relies in inexistence or occupation of the serial interface.',
'-301': 'Requested TCP/IP version errorRequested TCP/IP version error ',
'-302': 'Incorrect version number ',
'-303': 'Fail to get the protocol type',
'-304': 'Invalid SOCKET',
'-305': 'SOCKET error',
'-306': 'HOST error',
'-307': 'Connection attempt failed',
'10035': 'Resources temporarily unavailable.',
'10038': 'An operation was attempted on something that is not a socket.',
'10054': 'Connection reset by peer.',
'10060': 'Connection timed out.',
'10061': 'Connection refused.',
'10065': 'No route to host.',
}
EVENT_TYPES = {
'0': 'Normal Punch Open',
'1': 'Punch during Normal Open Time Zone',
'2': 'First Card Normal Open (Punch Card)',
'3': 'Multi-Card Open (Punching Card)',
'4': 'Emergency Password Open',
'5': 'Open during Normal Open Time Zone',
'6': 'Linkage Event Triggered',
'7': 'Cancel Alarm',
'8': 'Remote Opening',
'9': 'Remote Closing',
'10': 'Disable Intraday Normal Open Time Zone',
'11': 'Enable Intraday Normal Open Time Zone',
'12': 'Open Auxiliary Output',
'13': 'Close Auxiliary Output',
'14': 'Press Fingerprint Open',
'15': 'Multi-Card Open (Press Fingerprint)',
'16': 'Press Fingerprint during Normal Open Time Zone',
'17': 'Card plus Fingerprint Open',
'18': 'First Card Normal Open (Press Fingerprint)',
'19': 'First Card Normal Open (Card plus Fingerprint)',
'20': 'Too Short Punch Interval',
'21': 'Door Inactive Time Zone (Punch Card)',
'22': 'Illegal Time Zone',
'23': 'Access Denied',
'24': 'Anti-Passback',
'25': 'Interlock',
'26': 'Multi-Card Authentication (Punching Card)',
'27': 'Unregistered Card',
'28': 'Opening Timeout',
'29': 'Card Expired',
'30': 'Password Error',
'31': 'Too Short Fingerprint Pressing Interval',
'32': 'Multi-Card Authentication (Press Fingerprint)',
'33': 'Fingerprint Expired',
'34': 'Unregistered Fingerprint',
'35': 'Door Inactive Time Zone (Press Fingerprint)',
'36': 'Door Inactive Time Zone (Exit Button)',
'37': 'Failed to Close during Normal Open Time Zone',
'101': 'Duress Password Open',
'102': 'Opened Accidentally',
'103': 'Duress Fingerprint Open',
'200': 'Door Opened Correctly',
'201': 'Door Closed Correctly',
'202': 'Exit button Open',
'203': 'Multi-Card Open (Card plus Fingerprint)',
'204': 'Normal Open Time Zone Over',
'205': 'Remote Normal Opening',
'220': 'Auxiliary Input Disconnected',
'221': 'Auxiliary Input Shorted',
'255': 'Actually that obtain door status and alarm status',
}
ENTRY_EXIT_TYPES = {
'0': 'Entry',
'1': 'Exit',
'2': 'None'
}
class ZK400:
"""ZKAccess C3-400"""
relays = 8
relays_def = (
1, 2, 3, 4,
1, 2, 3, 4
)
groups_def = (
RelayGroup.aux, RelayGroup.aux, RelayGroup.aux, RelayGroup.aux,
RelayGroup.lock, RelayGroup.lock, RelayGroup.lock, RelayGroup.lock
)