zklib 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/zklib.rb +124 -0
- data/lib/zklib/attendance_management.rb +119 -0
- data/lib/zklib/connection_management.rb +23 -0
- data/lib/zklib/data_management.rb +37 -0
- data/lib/zklib/device_management.rb +91 -0
- data/lib/zklib/face_management.rb +40 -0
- data/lib/zklib/helper.rb +181 -0
- data/lib/zklib/pin_management.rb +22 -0
- data/lib/zklib/platform_management.rb +22 -0
- data/lib/zklib/serial_management.rb +22 -0
- data/lib/zklib/ssr_management.rb +22 -0
- data/lib/zklib/time_management.rb +47 -0
- data/lib/zklib/user_management.rb +199 -0
- data/lib/zklib/version.rb +3 -0
- data/lib/zklib/version_management.rb +58 -0
- data/lib/zklib/work_code_management.rb +22 -0
- data/zklib.gemspec +27 -0
- metadata +139 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
class Zklib
|
2
|
+
module DataManagement
|
3
|
+
# Free data for transmission
|
4
|
+
def free_data
|
5
|
+
execute_cmd(
|
6
|
+
command: CMD_FREE_DATA,
|
7
|
+
command_string: ''
|
8
|
+
) do |opts|
|
9
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
10
|
+
|
11
|
+
data = opts[:data]
|
12
|
+
if data.length > 7
|
13
|
+
data.split("\u0000").pop
|
14
|
+
else
|
15
|
+
puts 'ERROR: Invalid free data response'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Refresh data for transmission
|
21
|
+
def refresh_data
|
22
|
+
execute_cmd(
|
23
|
+
command: CMD_REFRESHDATA,
|
24
|
+
command_string: ''
|
25
|
+
) do |opts|
|
26
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
27
|
+
|
28
|
+
data = opts[:data]
|
29
|
+
if data.length > 7
|
30
|
+
data.split("\u0000").pop
|
31
|
+
else
|
32
|
+
puts 'ERROR: Invalid refresh data response'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Zklib
|
2
|
+
module DeviceManagement
|
3
|
+
DEVICE_NAME_KEYWORD = '~DeviceName'
|
4
|
+
DISABLE_DEVICE_KEYWORD = "\u0000\u0000"
|
5
|
+
|
6
|
+
# Disable attendance machine
|
7
|
+
def disable_device
|
8
|
+
execute_cmd(
|
9
|
+
command: CMD_DISABLEDEVICE,
|
10
|
+
command_string: DISABLE_DEVICE_KEYWORD
|
11
|
+
) do |opts|
|
12
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
13
|
+
|
14
|
+
data = opts[:data]
|
15
|
+
if data.length > 7
|
16
|
+
data.split("\u0000").pop
|
17
|
+
else
|
18
|
+
puts 'ERROR: Invalid disable device response'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Enable attendance machine
|
24
|
+
def enable_device
|
25
|
+
execute_cmd(
|
26
|
+
command: CMD_ENABLEDEVICE,
|
27
|
+
command_string: ''
|
28
|
+
) do |opts|
|
29
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
30
|
+
|
31
|
+
data = opts[:data]
|
32
|
+
if data.length > 7
|
33
|
+
data.split("\u0000").pop
|
34
|
+
else
|
35
|
+
puts 'ERROR: Invalid enable device response'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get device name
|
41
|
+
def get_device_name
|
42
|
+
execute_cmd(
|
43
|
+
command: CMD_DEVICE,
|
44
|
+
command_string: DEVICE_NAME_KEYWORD
|
45
|
+
) do |opts|
|
46
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
47
|
+
|
48
|
+
data = opts[:data]
|
49
|
+
if data.length > 8
|
50
|
+
data.split("\u0000").pop.tr("#{DEVICE_NAME_KEYWORD}=", '')
|
51
|
+
else
|
52
|
+
puts 'ERROR: Invalid device name response'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Turn off attendance machine
|
58
|
+
def power_off_device
|
59
|
+
execute_cmd(
|
60
|
+
command: CMD_POWEROFF,
|
61
|
+
command_string: ''
|
62
|
+
) do |opts|
|
63
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
64
|
+
|
65
|
+
data = opts[:data]
|
66
|
+
if data.length > 7
|
67
|
+
data.split("\u0000").pop
|
68
|
+
else
|
69
|
+
puts 'ERROR: Invalid power off device response'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Restart attendance machine
|
75
|
+
def restart_device
|
76
|
+
execute_cmd(
|
77
|
+
command: CMD_RESTART,
|
78
|
+
command_string: ''
|
79
|
+
) do |opts|
|
80
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
81
|
+
|
82
|
+
data = opts[:data]
|
83
|
+
if data.length > 7
|
84
|
+
data.split("\u0000").pop
|
85
|
+
else
|
86
|
+
puts 'ERROR: Invalid restart device response'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Zklib
|
2
|
+
module FaceManagement
|
3
|
+
TURN_FACE_OFF_KEYWORD = 'FaceFunOff'
|
4
|
+
TURN_FACE_ON_KEYWORD = 'FaceFunOn'
|
5
|
+
|
6
|
+
# Turn face off
|
7
|
+
def turn_face_off
|
8
|
+
execute_cmd(
|
9
|
+
command: CMD_DEVICE,
|
10
|
+
command_string: TURN_FACE_OFF_KEYWORD
|
11
|
+
) do |opts|
|
12
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
13
|
+
|
14
|
+
data = opts[:data]
|
15
|
+
if data.length > 7
|
16
|
+
data.split("\u0000").pop
|
17
|
+
else
|
18
|
+
puts 'ERROR: Invalid turn face off response'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Turn face on
|
24
|
+
def turn_face_on
|
25
|
+
execute_cmd(
|
26
|
+
command: CMD_DEVICE,
|
27
|
+
command_string: TURN_FACE_ON_KEYWORD
|
28
|
+
) do |opts|
|
29
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
30
|
+
|
31
|
+
data = opts[:data]
|
32
|
+
if data.length > 7
|
33
|
+
data.split("\u0000").pop
|
34
|
+
else
|
35
|
+
puts 'ERROR: Invalid turn face on response'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/zklib/helper.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
class Zklib
|
2
|
+
module Helper
|
3
|
+
# Check validity of response
|
4
|
+
#
|
5
|
+
# param options
|
6
|
+
# |_ data Data to check validity
|
7
|
+
def check_valid(options)
|
8
|
+
BinData::Uint16le.read(options[:data][0..-1]).snapshot == CMD_ACK_OK
|
9
|
+
end
|
10
|
+
|
11
|
+
# Create checksum for execution
|
12
|
+
#
|
13
|
+
# param options
|
14
|
+
# |_ data Data to create checksum
|
15
|
+
def create_checksum(options)
|
16
|
+
data = options[:data]
|
17
|
+
checksum = 0
|
18
|
+
|
19
|
+
(0...data.length).step(2) do |i|
|
20
|
+
checksum += (i == data.length - 1) ?
|
21
|
+
BinData::Uint8le.read(data[i]).snapshot :
|
22
|
+
BinData::Uint16le.read(data[i..-1]).snapshot
|
23
|
+
checksum %= USHRT_MAX
|
24
|
+
end
|
25
|
+
|
26
|
+
chksum = USHRT_MAX - checksum - 1
|
27
|
+
chksum
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create header for execution
|
31
|
+
#
|
32
|
+
# param options
|
33
|
+
# |_ command Command value
|
34
|
+
# |_ checksum Checksum
|
35
|
+
# |_ session_id Session ID
|
36
|
+
# |_ reply_id Reply ID
|
37
|
+
# |_ command_string Command string
|
38
|
+
def create_header(options)
|
39
|
+
header_buffer = StringIO.new
|
40
|
+
binary_writer = BinData::Uint16le.new
|
41
|
+
|
42
|
+
# Write command header
|
43
|
+
binary_writer.value = options[:command]
|
44
|
+
header_buffer.pos = 0
|
45
|
+
binary_writer.write(header_buffer)
|
46
|
+
|
47
|
+
# Write checksum header
|
48
|
+
binary_writer.value = options[:checksum]
|
49
|
+
header_buffer.pos = 2
|
50
|
+
binary_writer.write(header_buffer)
|
51
|
+
|
52
|
+
# Write session ID header
|
53
|
+
binary_writer.value = options[:session_id]
|
54
|
+
header_buffer.pos = 4
|
55
|
+
binary_writer.write(header_buffer)
|
56
|
+
|
57
|
+
# Write reply ID header
|
58
|
+
binary_writer.value = options[:reply_id]
|
59
|
+
header_buffer.pos = 6
|
60
|
+
binary_writer.write(header_buffer)
|
61
|
+
|
62
|
+
# Write command string header
|
63
|
+
header_buffer.pos = 8
|
64
|
+
header_buffer.write(options[:command_string])
|
65
|
+
|
66
|
+
# Rewrite checksum header
|
67
|
+
binary_writer.value = create_checksum(data: header_buffer.string)
|
68
|
+
header_buffer.pos = 2
|
69
|
+
binary_writer.write(header_buffer)
|
70
|
+
|
71
|
+
# Rewrite reply ID header
|
72
|
+
binary_writer.value = (options[:reply_id] + 1) % USHRT_MAX
|
73
|
+
header_buffer.pos = 6
|
74
|
+
binary_writer.write(header_buffer)
|
75
|
+
|
76
|
+
header_buffer.string
|
77
|
+
end
|
78
|
+
|
79
|
+
# Convert number of seconds to time
|
80
|
+
#
|
81
|
+
# param options
|
82
|
+
# |_ seconds Time in seconds to decode
|
83
|
+
def decode_time(options)
|
84
|
+
time = options[:seconds]
|
85
|
+
|
86
|
+
# Calculate second value
|
87
|
+
second = time % 60
|
88
|
+
time = (time - second) / 60
|
89
|
+
|
90
|
+
# Calculate minute value
|
91
|
+
minute = time % 60
|
92
|
+
time = (time - minute) / 60
|
93
|
+
|
94
|
+
# Calculate hour value
|
95
|
+
hour = time % 24
|
96
|
+
time = (time - hour) / 24
|
97
|
+
|
98
|
+
# Calculate day value
|
99
|
+
day = time % 31 + 1
|
100
|
+
time = (time - day + 1) / 31
|
101
|
+
|
102
|
+
# Calculate month value
|
103
|
+
month = time % 12 + 1
|
104
|
+
time = (time - month + 1) / 12
|
105
|
+
|
106
|
+
# Calculate year value
|
107
|
+
year = time + 2000
|
108
|
+
|
109
|
+
Time.new(year, month, day, hour, minute, second)
|
110
|
+
end
|
111
|
+
|
112
|
+
def decode_user_data(options)
|
113
|
+
data = options[:data]
|
114
|
+
|
115
|
+
{
|
116
|
+
uid: BinData::Uint16be.read(data[0..1]).snapshot,
|
117
|
+
role: BinData::Uint16le.read(data[2..3]).snapshot,
|
118
|
+
password: nil,
|
119
|
+
name: nil,
|
120
|
+
cardno: nil,
|
121
|
+
userid: nil
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
# Convert time to number of seconds
|
126
|
+
#
|
127
|
+
# param options
|
128
|
+
# |_ time Time object to encode
|
129
|
+
def encode_time(options)
|
130
|
+
time = options[:time]
|
131
|
+
|
132
|
+
((time.year % 100) * 12 * 31 + ((time.mon - 1) * 31) + time.day - 1) * (24 * 60 * 60) + (time.hour * 60 + time.min) * 60 + time.sec
|
133
|
+
end
|
134
|
+
|
135
|
+
# Execute command on attendance machine
|
136
|
+
#
|
137
|
+
# param options
|
138
|
+
# |_ command Command to execute
|
139
|
+
# |_ command_string Command string
|
140
|
+
def execute_cmd(options)
|
141
|
+
command = options[:command]
|
142
|
+
self.reply_id = USHRT_MAX - 1 if command == CMD_CONNECT
|
143
|
+
header = create_header(
|
144
|
+
command: command,
|
145
|
+
checksum: 0,
|
146
|
+
session_id: session_id,
|
147
|
+
reply_id: reply_id,
|
148
|
+
command_string: options[:command_string]
|
149
|
+
)
|
150
|
+
|
151
|
+
# Send command
|
152
|
+
socket = UDPSocket.new
|
153
|
+
socket.bind('0.0.0.0', inport)
|
154
|
+
socket.send(header, 0, ip, port)
|
155
|
+
self.data_recv = socket.recvfrom(USHRT_MAX)[0] if IO.select([socket], nil, nil, 10)
|
156
|
+
socket.close
|
157
|
+
|
158
|
+
# Callback
|
159
|
+
if data_recv && data_recv.length > 0
|
160
|
+
self.session_id = BinData::Uint16le.read(data_recv[4..-1]).snapshot
|
161
|
+
self.reply_id = BinData::Uint16le.read(data_recv[6..-1]).snapshot
|
162
|
+
|
163
|
+
yield(valid: true, data: data_recv) if block_given?
|
164
|
+
else
|
165
|
+
yield(valid: false, error: 'Empty response') if block_given?
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Receive data from non-blocking socket
|
170
|
+
#
|
171
|
+
# param options
|
172
|
+
# |_ socket Socket to receive data from
|
173
|
+
def receive_nonblock(options)
|
174
|
+
return options[:socket].recvfrom_nonblock(USHRT_MAX)
|
175
|
+
rescue IO::WaitReadable
|
176
|
+
IO.select([options[:socket]])
|
177
|
+
|
178
|
+
retry
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Zklib
|
2
|
+
module PINManagement
|
3
|
+
PIN_WIDTH_KEYWORD = '~PIN2Width'
|
4
|
+
|
5
|
+
# Get PIN width
|
6
|
+
def get_pin_width
|
7
|
+
execute_cmd(
|
8
|
+
command: CMD_DEVICE,
|
9
|
+
command_string: PIN_WIDTH_KEYWORD
|
10
|
+
) do |opts|
|
11
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
12
|
+
|
13
|
+
data = opts[:data]
|
14
|
+
if data.length > 8
|
15
|
+
data.split("\u0000").pop.tr("#{PIN_WIDTH_KEYWORD}=", '')
|
16
|
+
else
|
17
|
+
puts 'ERROR: Invalid PIN width response'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Zklib
|
2
|
+
module PlatformManagement
|
3
|
+
PLATFORM_KEYWORD = '~Platform'
|
4
|
+
|
5
|
+
# Get platform
|
6
|
+
def get_platform
|
7
|
+
execute_cmd(
|
8
|
+
command: CMD_DEVICE,
|
9
|
+
command_string: PLATFORM_KEYWORD
|
10
|
+
) do |opts|
|
11
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
12
|
+
|
13
|
+
data = opts[:data]
|
14
|
+
if data.length > 8
|
15
|
+
data.split("\u0000").pop.tr("#{PLATFORM_KEYWORD}=", '')
|
16
|
+
else
|
17
|
+
puts 'ERROR: Invalid platform response'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Zklib
|
2
|
+
module SerialManagement
|
3
|
+
SERIAL_KEYWORD = '~SerialNumber'
|
4
|
+
|
5
|
+
# Get serial number of attendance machine
|
6
|
+
def get_serial_number
|
7
|
+
execute_cmd(
|
8
|
+
command: CMD_DEVICE,
|
9
|
+
command_string: SERIAL_KEYWORD
|
10
|
+
) do |opts|
|
11
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
12
|
+
|
13
|
+
data = opts[:data]
|
14
|
+
if data.length > 8
|
15
|
+
data.split("\u0000").pop.tr("#{SERIAL_KEYWORD}=", '')
|
16
|
+
else
|
17
|
+
puts 'ERROR: Invalid serial number response'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Zklib
|
2
|
+
module SSRManagement
|
3
|
+
SSR_KEYWORD = '~SSR'
|
4
|
+
|
5
|
+
# Get SSR
|
6
|
+
def get_ssr
|
7
|
+
execute_cmd(
|
8
|
+
command: CMD_DEVICE,
|
9
|
+
command_string: SSR_KEYWORD
|
10
|
+
) do |opts|
|
11
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
12
|
+
|
13
|
+
data = opts[:data]
|
14
|
+
if data.length > 8
|
15
|
+
data.split("\u0000").pop.tr("#{SSR_KEYWORD}=", '')
|
16
|
+
else
|
17
|
+
puts 'ERROR: Invalid SSR response'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Zklib
|
2
|
+
module TimeManagement
|
3
|
+
# Get current time of attendance machine
|
4
|
+
def get_time
|
5
|
+
execute_cmd(
|
6
|
+
command: CMD_GET_TIME,
|
7
|
+
command_string: ''
|
8
|
+
) do |opts|
|
9
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
10
|
+
|
11
|
+
data = opts[:data]
|
12
|
+
if data.length > 8
|
13
|
+
decode_time(seconds: BinData::Uint32le.read(data[8..-1]).snapshot)
|
14
|
+
else
|
15
|
+
puts 'ERROR: Invalid time response'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set current time for attendance machine
|
21
|
+
def set_time(time = Time.now)
|
22
|
+
seconds = encode_time(time: time)
|
23
|
+
command_buffer = StringIO.new
|
24
|
+
binary_writer = BinData::Uint32le.new
|
25
|
+
|
26
|
+
# Write command string
|
27
|
+
binary_writer.value = seconds
|
28
|
+
command_buffer.pos = 0
|
29
|
+
binary_writer.write(command_buffer)
|
30
|
+
command_string = command_buffer.string
|
31
|
+
|
32
|
+
execute_cmd(
|
33
|
+
command: CMD_SET_TIME,
|
34
|
+
command_string: command_string
|
35
|
+
) do |opts|
|
36
|
+
return puts "ERROR: #{options[:error]}" unless opts[:valid]
|
37
|
+
|
38
|
+
data = opts[:data]
|
39
|
+
if data.length > 7
|
40
|
+
data.split("\u0000").pop
|
41
|
+
else
|
42
|
+
puts 'ERROR: Invalid time response'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|