diff --git a/app/Http/Controllers/BiometricAttendanceController.php b/app/Http/Controllers/BiometricAttendanceController.php index ba3cdb112..e1d189b72 100644 --- a/app/Http/Controllers/BiometricAttendanceController.php +++ b/app/Http/Controllers/BiometricAttendanceController.php @@ -42,7 +42,7 @@ class BiometricAttendanceController extends Controller if ($branches->isNotEmpty()) { foreach ($branches as $branch) { try { - $zk = new \Rats\Zkteco\Lib\Zkteco($branch->zkteco_ip); + $zk = new \App\Library\ZKTeco\ZKTeco($branch->zkteco_ip); if ($zk->connect()) { $raw_attendance = $zk->getAttendance(); $startTs = strtotime(strlen($start_date) == 10 ? $start_date . ' 00:00:00' : $start_date); @@ -67,7 +67,7 @@ class BiometricAttendanceController extends Controller } else if (!empty($api_urls)) { if ($isDirectIP) { try { - $zk = new \Rats\Zkteco\Lib\Zkteco($api_urls); + $zk = new \App\Library\ZKTeco\ZKTeco($api_urls); if ($zk->connect()) { $raw_attendance = $zk->getAttendance(); $startTs = strtotime(strlen($start_date) == 10 ? $start_date . ' 00:00:00' : $start_date); @@ -234,7 +234,7 @@ class BiometricAttendanceController extends Controller if ($isDirectIP) { try { - $zk = new \Rats\Zkteco\Lib\Zkteco($api_urls); + $zk = new \App\Library\ZKTeco\ZKTeco($api_urls); if ($zk->connect()) { $raw_attendance = $zk->getAttendance(); foreach ($raw_attendance as $record) { @@ -351,7 +351,7 @@ class BiometricAttendanceController extends Controller if ($branches->isNotEmpty()) { foreach ($branches as $branch) { try { - $zk = new \Rats\Zkteco\Lib\Zkteco($branch->zkteco_ip); + $zk = new \App\Library\ZKTeco\ZKTeco($branch->zkteco_ip); if ($zk->connect()) { $raw_attendance = $zk->getAttendance(); $startTs = strtotime($start_date . ' 00:00:00'); @@ -374,7 +374,7 @@ class BiometricAttendanceController extends Controller } else if (!empty($api_urls)) { if ($isDirectIP) { try { - $zk = new \Rats\Zkteco\Lib\Zkteco($api_urls); + $zk = new \App\Library\ZKTeco\ZKTeco($api_urls); if ($zk->connect()) { $raw_attendance = $zk->getAttendance(); $startTs = strtotime($start_date . ' 00:00:00'); diff --git a/app/Http/Controllers/BranchController.php b/app/Http/Controllers/BranchController.php index b51a16648..24f364d04 100644 --- a/app/Http/Controllers/BranchController.php +++ b/app/Http/Controllers/BranchController.php @@ -202,7 +202,7 @@ class BranchController extends Controller ]); try { - $zk = new \Rats\Zkteco\Lib\ZKTeco($request->zkteco_ip, 4370); + $zk = new \App\Library\ZKTeco\ZKTeco($request->zkteco_ip, 4370); if ($zk->connect()) { $deviceName = $zk->deviceName(); $serialNumber = $zk->serialNumber(); diff --git a/app/Library/ZKTeco/Helper/Attendance.php b/app/Library/ZKTeco/Helper/Attendance.php new file mode 100644 index 000000000..a8dc8cb5f --- /dev/null +++ b/app/Library/ZKTeco/Helper/Attendance.php @@ -0,0 +1,72 @@ +_section = __METHOD__; + + $command = Util::CMD_ATT_LOG_RRQ; + $command_string = ''; + + $session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA); + if ($session === false) { + return []; + } + + $attData = Util::recData($self); + + $attendance = []; + if (!empty($attData)) { + $attData = substr($attData, 10); + + while (strlen($attData) > 40) { + $u = unpack('H78', substr($attData, 0, 39)); + + $u1 = hexdec(substr($u[1], 4, 2)); + $u2 = hexdec(substr($u[1], 6, 2)); + $uid = $u1 + ($u2 * 256); + $id = hex2bin(substr($u[1], 8, 18)); + $id = str_replace(chr(0), '', $id); + $state = hexdec(substr($u[1], 56, 2)); + $timestamp = Util::decodeTime(hexdec(Util::reverseHex(substr($u[1], 58, 8)))); + $type = hexdec(Util::reverseHex(substr($u[1], 66, 2 ))); + + $attendance[] = [ + 'uid' => $uid, + 'id' => $id, + 'state' => $state, + 'timestamp' => $timestamp, + 'type' => $type + ]; + + $attData = substr($attData, 40); + } + + } + + return $attendance; + } + + /** + * @param ZKTeco $self + * @return bool|mixed + */ + static public function clear(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_CLEAR_ATT_LOG; + $command_string = ''; + + return $self->_command($command, $command_string); + } +} diff --git a/app/Library/ZKTeco/Helper/Connect.php b/app/Library/ZKTeco/Helper/Connect.php new file mode 100644 index 000000000..25bbcab75 --- /dev/null +++ b/app/Library/ZKTeco/Helper/Connect.php @@ -0,0 +1,83 @@ +_section = __METHOD__; + + $command = Util::CMD_CONNECT; + $command_string = ''; + $chksum = 0; + $session_id = 0; + $reply_id = -1 + Util::USHRT_MAX; + + $buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string); + + socket_sendto($self->_zkclient, $buf, strlen($buf), 0, $self->_ip, $self->_port); + + try { + @socket_recvfrom($self->_zkclient, $self->_data_recv, 1024, 0, $self->_ip, $self->_port); + if (strlen($self->_data_recv) > 0) { + $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6', substr($self->_data_recv, 0, 8)); + + $session = hexdec($u['h6'] . $u['h5']); + if (empty($session)) { + return false; + } + + $self->_session_id = $session; + return Util::checkValid($self->_data_recv); + } else { + return false; + } + } catch (ErrorException $e) { + return false; + } catch (Exception $e) { + return false; + } + } + + /** + * @param ZKTeco $self + * @return bool + */ + static public function disconnect(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_EXIT; + $command_string = ''; + $chksum = 0; + $session_id = $self->_session_id; + + $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($self->_data_recv, 0, 8)); + $reply_id = hexdec($u['h8'] . $u['h7']); + + $buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string); + + + socket_sendto($self->_zkclient, $buf, strlen($buf), 0, $self->_ip, $self->_port); + try { + @socket_recvfrom($self->_zkclient, $self->_data_recv, 1024, 0, $self->_ip, $self->_port); + + $self->_session_id = 0; + return Util::checkValid($self->_data_recv); + } catch (ErrorException $e) { + return false; + } catch (Exception $e) { + return false; + } + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/Device.php b/app/Library/ZKTeco/Helper/Device.php new file mode 100644 index 000000000..0fbb01c85 --- /dev/null +++ b/app/Library/ZKTeco/Helper/Device.php @@ -0,0 +1,151 @@ +_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = '~DeviceName'; + + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @return bool|mixed + */ + static public function enable(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_ENABLE_DEVICE; + $command_string = ''; + + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @return bool|mixed + */ + static public function disable(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_DISABLE_DEVICE; + $command_string = chr(0) . chr(0); + + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @return bool|mixed *** this will turn off the device + */ + public static function powerOff(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_POWEROFF; + $command_string = chr(0) . chr(0); + return $self->_command($command, $command_string); + } + + + /** + * @param ZKTeco $self + * @return bool|mixed *** this will restart the device + */ + public static function restart(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_RESTART; + $command_string = chr(0) . chr(0); + return $self->_command($command, $command_string); + } + + + /** + * @param ZKTeco $self + * @return bool|mixed *** this will sleep the device + */ + public static function sleep(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_SLEEP; + $command_string = chr(0) . chr(0); + return $self->_command($command, $command_string); + } + + + /** + * @param ZKTeco $self + * @return bool|mixed *** this will resume the device from sleep + */ + public static function resume(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_RESUME; + $command_string = chr(0) . chr(0); + return $self->_command($command, $command_string); + } + + + /** + * @param ZKTeco $self + * @return bool|mixed *** this will play voice "Thank you" + */ + public static function testVoice(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_TESTVOICE; + $command_string = chr(0) . chr(0); + return $self->_command($command, $command_string); + } + + + /** + * @param ZKTeco $self + * @return bool|mixed *** this will clear the LCD screen + */ + public static function clearLCD(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_CLEAR_LCD; + return $self->_command($command, ''); + } + + + /** + * @param ZKTeco $self + * @param $rank *** Line number of text + * @param $text *** Text which will display in the LCD screen + * @return bool|mixed *** this will write text into the LCD + */ + public static function writeLCD(ZKTeco $self, $rank, $text) + { + $self->_section = __METHOD__; + + $command = Util::CMD_WRITE_LCD; + $byte1 = chr((int)($rank % 256)); + $byte2 = chr((int)($rank >> 8)); + $byte3 = chr(0); + $command_string = $byte1.$byte2.$byte3.' '.$text; + return $self->_command($command, $command_string); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/Face.php b/app/Library/ZKTeco/Helper/Face.php new file mode 100644 index 000000000..6d8594072 --- /dev/null +++ b/app/Library/ZKTeco/Helper/Face.php @@ -0,0 +1,23 @@ +_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = 'FaceFunOn'; + + return $self->_command($command, $command_string); + } +} + diff --git a/app/Library/ZKTeco/Helper/Fingerprint.php b/app/Library/ZKTeco/Helper/Fingerprint.php new file mode 100644 index 000000000..e0ef1395a --- /dev/null +++ b/app/Library/ZKTeco/Helper/Fingerprint.php @@ -0,0 +1,166 @@ +_section = __METHOD__; + + $data = []; + //fingers of the hands + for ($i = 0; $i <= 9; $i++) { + $finger = new Fingerprint(); + $tmp = $finger->_getFinger($self, $uid, $i); + if ($tmp['size'] > 0) { + $data[$i] = $tmp['tpl']; + } + unset($tmp); + } + return $data; + } + + + /** + * @param ZKTeco $self + * @param integer $uid Unique Employee ID in ZK device + * @param integer $finger Finger ID (0-9) + * @return array + */ + private function _getFinger(ZKTeco $self, $uid, $finger) + { + $command = Util::CMD_USER_TEMP_RRQ; + $byte1 = chr((int)($uid % 256)); + $byte2 = chr((int)($uid >> 8)); + $command_string = $byte1 . $byte2 . chr($finger); + + $ret = [ + 'size' => 0, + 'tpl' => '' + ]; + + $session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA); + if ($session === false) { + return $ret; + } + + $data = Util::recData($self, 10, false); + + if (!empty($data)) { + $templateSize = strlen($data); + $prefix = chr($templateSize % 256) . chr(round($templateSize / 256)) . $byte1 . $byte2 . chr($finger) . chr(1); + $data = $prefix . $data; + if (strlen($templateSize) > 0) { + $ret['size'] = $templateSize; + $ret['tpl'] = $data; + } + } + + return $ret; + } + + /** + * TODO: Still can not set fingerprint. Need more documentation about it... + * + * @param ZKTeco $self + * @param int $uid Unique Employee ID in ZK device + * @param array $data Binary fingerprint data array (where key is finger ID (0-9) same like returned array from 'get' method) + * @return int Count of added fingerprints + */ + static public function set(ZKTeco $self, $uid, array $data) + { + $self->_section = __METHOD__; + + + $count = 0; + foreach ($data as $finger => $item) { + $allowSet = true; + $fingerPrint = new Fingerprint(); + if ($fingerPrint->_checkFinger($self, $uid, $finger) === true) { + $allowSet = $fingerPrint->_removeFinger($self, $uid, $finger); + } + if ($allowSet === true && $fingerPrint->_setFinger($self, $item) === true) { + $count++; + } + } + + return $count; + } + + /** + * @param ZKTeco $self + * @param string $data Binary fingerprint data item + * @return bool|mixed + */ + private function _setFinger(ZKTeco $self, $data) + { + $command = Util::CMD_USER_TEMP_WRQ; + $command_string = $data; + + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @param int $uid Unique Employee ID in ZK device + * @param array $data Fingers ID array (0-9) + * @return int Count of deleted fingerprints + */ + static public function remove(ZKTeco $self, $uid, array $data) + { + $self->_section = __METHOD__; + + $count = 0; + foreach ($data as $finger) { + $fingerPrint = new Fingerprint(); + if ($fingerPrint->_checkFinger($self, $uid, $finger) === true) { + if ($fingerPrint->_removeFinger($self, $uid, $finger) === true) { + $count++; + } + } + } + + return $count; + } + + /** + * @param ZKTeco $self + * @param int $uid Unique Employee ID in ZK device + * @param int $finger Finger ID (0-9) + * @return bool + */ + private function _removeFinger(ZKTeco $self, $uid, $finger) + { + $command = Util::CMD_DELETE_USER_TEMP; + $byte1 = chr((int)($uid % 256)); + $byte2 = chr((int)($uid >> 8)); + $command_string = ($byte1 . $byte2) . chr($finger); + + $self->_command($command, $command_string); + $fingerPrint = new Fingerprint(); + return !($fingerPrint->_checkFinger($self, $uid, $finger)); + } + + /** + * @param ZKTeco $self + * @param int $uid Unique Employee ID in ZK device + * @param int $finger Finger ID (0-9) + * @return bool Returned true if exist + */ + private function _checkFinger(ZKTeco $self, $uid, $finger) + { + $fingerPrint = new Fingerprint(); + $res = $fingerPrint->_getFinger($self, $uid, $finger); + return (bool)($res['size'] > 0); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/Os.php b/app/Library/ZKTeco/Helper/Os.php new file mode 100644 index 000000000..d6c4b7a0c --- /dev/null +++ b/app/Library/ZKTeco/Helper/Os.php @@ -0,0 +1,22 @@ +_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = '~OS'; + + return $self->_command($command, $command_string); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/Pin.php b/app/Library/ZKTeco/Helper/Pin.php new file mode 100644 index 000000000..99245c193 --- /dev/null +++ b/app/Library/ZKTeco/Helper/Pin.php @@ -0,0 +1,22 @@ +_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = '~PIN2Width'; + + return $self->_command($command, $command_string); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/Platform.php b/app/Library/ZKTeco/Helper/Platform.php new file mode 100644 index 000000000..e60269cde --- /dev/null +++ b/app/Library/ZKTeco/Helper/Platform.php @@ -0,0 +1,36 @@ +_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = '~Platform'; + + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @return bool|mixed + */ + static public function getVersion(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = '~ZKFPVersion'; + + return $self->_command($command, $command_string); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/SerialNumber.php b/app/Library/ZKTeco/Helper/SerialNumber.php new file mode 100644 index 000000000..16211add1 --- /dev/null +++ b/app/Library/ZKTeco/Helper/SerialNumber.php @@ -0,0 +1,22 @@ +_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = '~SerialNumber'; + + return $self->_command($command, $command_string); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/Ssr.php b/app/Library/ZKTeco/Helper/Ssr.php new file mode 100644 index 000000000..b0debd0eb --- /dev/null +++ b/app/Library/ZKTeco/Helper/Ssr.php @@ -0,0 +1,22 @@ +_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = '~SSR'; + + return $self->_command($command, $command_string); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/Time.php b/app/Library/ZKTeco/Helper/Time.php new file mode 100644 index 000000000..babdef96d --- /dev/null +++ b/app/Library/ZKTeco/Helper/Time.php @@ -0,0 +1,44 @@ +_section = __METHOD__; + + $command = Util::CMD_SET_TIME; + $command_string = pack('I', Util::encodeTime($t)); + + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @return bool|mixed + */ + static public function get(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_GET_TIME; + $command_string = ''; + + $ret = $self->_command($command, $command_string); + + if ($ret) { + return Util::decodeTime(hexdec(Util::reverseHex(bin2hex($ret)))); + } else { + return false; + } + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/User.php b/app/Library/ZKTeco/Helper/User.php new file mode 100644 index 000000000..93bf34396 --- /dev/null +++ b/app/Library/ZKTeco/Helper/User.php @@ -0,0 +1,161 @@ +_section = __METHOD__; + + if ( + (int)$uid === 0 || + (int)$uid > Util::USHRT_MAX || + strlen($userid) > 9 || + strlen($name) > 24 || + strlen($password) > 8 || + strlen($cardno) > 10 + ) { + return false; + } + + $command = Util::CMD_SET_USER; + $byte1 = chr((int)($uid % 256)); + $byte2 = chr((int)($uid >> 8)); + $cardno = hex2bin(Util::reverseHex(dechex($cardno))); + + $command_string = implode('', [ + $byte1, + $byte2, + chr($role), + str_pad($password, 8, chr(0)), + str_pad($name, 24, chr(0)), + str_pad($cardno, 4, chr(0)), + str_pad(chr(1), 9, chr(0)), + str_pad($userid, 9, chr(0)), + str_repeat(chr(0), 15) + ]); +// die($command_string); + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @return array [userid, name, cardno, uid, role, password] + */ + static public function get(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_USER_TEMP_RRQ; + $command_string = chr(Util::FCT_USER); + + $session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA); + if ($session === false) { + return []; + } + + $userData = Util::recData($self); + + $users = []; + if (!empty($userData)) { + $userData = substr($userData, 11); + + while (strlen($userData) > 72) { + $u = unpack('H144', substr($userData, 0, 72)); + + $u1 = hexdec(substr($u[1], 2, 2)); + $u2 = hexdec(substr($u[1], 4, 2)); + $uid = $u1 + ($u2 * 256); + $cardno = hexdec(substr($u[1], 78, 2) . substr($u[1], 76, 2) . substr($u[1], 74, 2) . substr($u[1], 72, 2)) . ' '; + $role = hexdec(substr($u[1], 6, 2)) . ' '; + $password = hex2bin(substr($u[1], 8, 16)) . ' '; + $name = hex2bin(substr($u[1], 24, 74)) . ' '; + $userid = hex2bin(substr($u[1], 98, 72)) . ' '; + + //Clean up some messy characters from the user name + $password = explode(chr(0), $password, 2); + $password = $password[0]; + $userid = explode(chr(0), $userid, 2); + $userid = $userid[0]; + $name = explode(chr(0), $name, 3); + $name = utf8_encode($name[0]); + $cardno = str_pad($cardno, 11, '0', STR_PAD_LEFT); + + if ($name == '') { + $name = $userid; + } + + $users[$userid] = [ + 'uid' => $uid, + 'userid' => $userid, + 'name' => $name, + 'role' => intval($role), + 'password' => $password, + 'cardno' => $cardno, + ]; + + $userData = substr($userData, 72); + } + } + + return $users; + } + + /** + * @param ZKTeco $self + * @return bool|mixed + */ + static public function clear(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_CLEAR_DATA; + $command_string = ''; + + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @return bool|mixed + */ + static public function clearAdmin(ZKTeco $self) + { + $self->_section = __METHOD__; + + $command = Util::CMD_CLEAR_ADMIN; + $command_string = ''; + + return $self->_command($command, $command_string); + } + + /** + * @param ZKTeco $self + * @param integer $uid + * @return bool|mixed + */ + static public function remove(ZKTeco $self, $uid) + { + $self->_section = __METHOD__; + + $command = Util::CMD_DELETE_USER; + $byte1 = chr((int)($uid % 256)); + $byte2 = chr((int)($uid >> 8)); + $command_string = ($byte1 . $byte2); + + return $self->_command($command, $command_string); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/Util.php b/app/Library/ZKTeco/Helper/Util.php new file mode 100644 index 000000000..0605d2561 --- /dev/null +++ b/app/Library/ZKTeco/Helper/Util.php @@ -0,0 +1,427 @@ + (int)date('Y', $timestamp), + 'month' => (int)date('m', $timestamp), + 'day' => (int)date('d', $timestamp), + 'hour' => (int)date('H', $timestamp), + 'minute' => (int)date('i', $timestamp), + 'second' => (int)date('s', $timestamp), + ]; + + $d = (($t->year % 100) * 12 * 31 + (($t->month - 1) * 31) + $t->day - 1) * + (24 * 60 * 60) + ($t->hour * 60 + $t->minute) * 60 + $t->second; + + return $d; + } + + /** + * Decode a timestamp retrieved from the timeclock + * copied from zkemsdk.c - DecodeTime + * + * @param int|string $t + * @return false|string Format: "Y-m-d H:i:s" + */ + static public function decodeTime($t) + { + $second = $t % 60; + $t = $t / 60; + + $minute = $t % 60; + $t = $t / 60; + + $hour = $t % 24; + $t = $t / 24; + + $day = $t % 31 + 1; + $t = $t / 31; + + $month = $t % 12 + 1; + $t = $t / 12; + + $year = floor($t + 2000); + + $d = date('Y-m-d H:i:s', strtotime( + $year . '-' . $month . '-' . $day . ' ' . $hour . ':' . $minute . ':' . $second + )); + + return $d; + } + + /** + * @param string $hex + * @return string + */ + static public function reverseHex($hex) + { + $tmp = ''; + + for ($i = strlen($hex); $i >= 0; $i--) { + $tmp .= substr($hex, $i, 2); + $i--; + } + + return $tmp; + } + + /** + * Checks a returned packet to see if it returned self::CMD_PREPARE_DATA, + * indicating that data packets are to be sent + * Returns the amount of bytes that are going to be sent + * + * @param ZKTeco $self + * @return bool|number + */ + static public function getSize(ZKTeco $self) + { + $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($self->_data_recv, 0, 8)); + $command = hexdec($u['h2'] . $u['h1']); + + if ($command == self::CMD_PREPARE_DATA) { + $u = unpack('H2h1/H2h2/H2h3/H2h4', substr($self->_data_recv, 8, 4)); + $size = hexdec($u['h4'] . $u['h3'] . $u['h2'] . $u['h1']); + return $size; + } else { + return false; + } + } + + /** + * This function calculates the chksum of the packet to be sent to the + * time clock + * Copied from zkemsdk.c + * + * @inheritdoc + */ + static public function createChkSum($p) + { + $l = count($p); + $chksum = 0; + $i = $l; + $j = 1; + while ($i > 1) { + $u = unpack('S', pack('C2', $p['c' . $j], $p['c' . ($j + 1)])); + + $chksum += $u[1]; + + if ($chksum > self::USHRT_MAX) { + $chksum -= self::USHRT_MAX; + } + $i -= 2; + $j += 2; + } + + if ($i) { + $chksum = $chksum + $p['c' . strval(count($p))]; + } + + while ($chksum > self::USHRT_MAX) { + $chksum -= self::USHRT_MAX; + } + + if ($chksum > 0) { + $chksum = -($chksum); + } else { + $chksum = abs($chksum); + } + + $chksum -= 1; + while ($chksum < 0) { + $chksum += self::USHRT_MAX; + } + + return pack('S', $chksum); + } + + /** + * This function puts a the parts that make up a packet together and + * packs them into a byte string + * + * @inheritdoc + */ + static public function createHeader($command, $chksum, $session_id, $reply_id, $command_string) + { + $buf = pack('SSSS', $command, $chksum, $session_id, $reply_id) . $command_string; + + $buf = unpack('C' . (8 + strlen($command_string)) . 'c', $buf); + + $u = unpack('S', self::createChkSum($buf)); + + if (is_array($u)) { + $u = reset($u); + } + $chksum = $u; + + $reply_id += 1; + + if ($reply_id >= self::USHRT_MAX) { + $reply_id -= self::USHRT_MAX; + } + + $buf = pack('SSSS', $command, $chksum, $session_id, $reply_id); + + return $buf . $command_string; + + } + + /** + * Checks a returned packet to see if it returned Util::CMD_ACK_OK, + * indicating success + * + * @inheritdoc + */ + static public function checkValid($reply) + { + $u = unpack('H2h1/H2h2', substr($reply, 0, 8)); + + $command = hexdec($u['h2'] . $u['h1']); + /** TODO: Some device can return 'Connection unauthorized' then should check also */ + if ($command == self::CMD_ACK_OK || $command == self::CMD_ACK_UNAUTH) { + return true; + } else { + return false; + } + } + + /** + * Get User Role string + * @param integer $role + * @return string + */ + static public function getUserRole($role) + { + switch ($role) { + case self::LEVEL_USER: + $ret = 'User'; + break; + case self::LEVEL_ADMIN: + $ret = 'Admin'; + break; + default: + $ret = 'Unknown'; + } + + return $ret; + } + + /** + * Get Attendance State string + * @param integer $state + * @return string + */ + static public function getAttState($state) + { + switch ($state) { + case self::ATT_STATE_FINGERPRINT: + $ret = 'Fingerprint'; + break; + case self::ATT_STATE_PASSWORD: + $ret = 'Password'; + break; + case self::ATT_STATE_CARD: + $ret = 'Card'; + break; + default: + $ret = 'Unknown'; + } + + return $ret; + } + + /** + * Get Attendance Type string + * @param integer $type + * @return string + */ + static public function getAttType($type) + { + switch ($type) { + case self::ATT_TYPE_CHECK_IN: + $ret = 'Check-in'; + break; + case self::ATT_TYPE_CHECK_OUT: + $ret = 'Check-out'; + break; + case self::ATT_TYPE_OVERTIME_IN: + $ret = 'Overtime-in'; + break; + case self::ATT_TYPE_OVERTIME_OUT: + $ret = 'Overtime-out'; + break; + default: + $ret = 'Undefined'; + } + + return $ret; + } + + /** + * Receive data from device + * @param ZKTeco $self + * @param int $maxErrors + * @param bool $first if 'true' don't remove first 4 bytes for first row + * @return string + */ + static public function recData(ZKTeco $self, $maxErrors = 10, $first = true) + { + $data = ''; + $bytes = self::getSize($self); + + if ($bytes) { + $received = 0; + $errors = 0; + + while ($bytes > $received) { + $ret = @socket_recvfrom($self->_zkclient, $dataRec, 1032, 0, $self->_ip, $self->_port); + + if ($ret === false) { + if ($errors < $maxErrors) { + //try again if false + $errors++; + sleep(1); + continue; + } else { + //return empty if has maximum count of errors + self::logReceived($self, $received, $bytes); + unset($data); + return ''; + } + } + + if ($first === false) { + //The first 4 bytes don't seem to be related to the user + $dataRec = substr($dataRec, 8); + } + + $data .= $dataRec; + $received += strlen($dataRec); + + unset($dataRec); + $first = false; + } + + //flush socket + @socket_recvfrom($self->_zkclient, $dataRec, 1024, 0, $self->_ip, $self->_port); + unset($dataRec); + } + + return $data; + } + + /** + * @param ZKTeco $self + * @param int $received + * @param int $bytes + */ + static private function logReceived(ZKTeco $self, $received, $bytes) + { + self::logger($self, 'Received: ' . $received . ' of ' . $bytes . ' bytes'); + } + + /** + * Write log + * @param ZKTeco $self + * @param string $str + */ + static private function logger(ZKTeco $self, $str) + { + if (defined('ZK_LIB_LOG')) { + //use constant if defined + $log = ZK_LIB_LOG; + } else { + $dir = dirname(dirname(__FILE__)); + $log = $dir . '/logs/error.log'; + } + + $row = '<' . $self->_ip . '> [' . date('d.m.Y H:i:s') . '] '; + $row .= (empty($self->_section) ? '' : '(' . $self->_section . ') '); + $row .= $str; + $row .= PHP_EOL; + + file_put_contents($log, $row, FILE_APPEND); + } +} diff --git a/app/Library/ZKTeco/Helper/Version.php b/app/Library/ZKTeco/Helper/Version.php new file mode 100644 index 000000000..922b9c54c --- /dev/null +++ b/app/Library/ZKTeco/Helper/Version.php @@ -0,0 +1,23 @@ +_section = __METHOD__; + + $command = Util::CMD_VERSION; + $command_string = ''; + + return $self->_command($command, $command_string); + } +} \ No newline at end of file diff --git a/app/Library/ZKTeco/Helper/WorkCode.php b/app/Library/ZKTeco/Helper/WorkCode.php new file mode 100644 index 000000000..274de63ef --- /dev/null +++ b/app/Library/ZKTeco/Helper/WorkCode.php @@ -0,0 +1,22 @@ +_section = __METHOD__; + + $command = Util::CMD_DEVICE; + $command_string = 'WorkCode'; + + return $self->_command($command, $command_string); + } +} diff --git a/app/Library/ZKTeco/ZKTeco.php b/app/Library/ZKTeco/ZKTeco.php new file mode 100644 index 000000000..43f570976 --- /dev/null +++ b/app/Library/ZKTeco/ZKTeco.php @@ -0,0 +1,435 @@ +_ip = $ip; + $this->_port = $port; + + $this->_zkclient = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + + $timeout = array('sec' => 60, 'usec' => 500000); + socket_set_option($this->_zkclient, SOL_SOCKET, SO_RCVTIMEO, $timeout); + + } + + /** + * Create and send command to device + * + * @param string $command + * @param string $command_string + * @param string $type + * @return bool|mixed + */ + public function _command($command, $command_string, $type = Util::COMMAND_TYPE_GENERAL) + { + $chksum = 0; + $session_id = $this->_session_id; + + $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($this->_data_recv, 0, 8)); + $reply_id = hexdec($u['h8'] . $u['h7']); + + $buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string); + + socket_sendto($this->_zkclient, $buf, strlen($buf), 0, $this->_ip, $this->_port); + + try { + @socket_recvfrom($this->_zkclient, $this->_data_recv, 1024, 0, $this->_ip, $this->_port); + + $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6', substr($this->_data_recv, 0, 8)); + + $ret = false; + $session = hexdec($u['h6'] . $u['h5']); + + if ($type === Util::COMMAND_TYPE_GENERAL && $session_id === $session) { + $ret = substr($this->_data_recv, 8); + } else if ($type === Util::COMMAND_TYPE_DATA && !empty($session)) { + $ret = $session; + } + + return $ret; + } catch (ErrorException $e) { + return false; + } catch (Exception $e) { + return false; + } + } + + /** + * Connect to device + * + * @return bool + */ + public function connect() + { + return Connect::connect($this); + } + + /** + * Disconnect from device + * + * @return bool + */ + public function disconnect() + { + return Connect::disconnect($this); + } + + /** + * Get device version + * + * @return bool|mixed + */ + public function version() + { + return Version::get($this); + } + + /** + * Get OS version + * + * @return bool|mixed + */ + public function osVersion() + { + return Os::get($this); + } + + /** + * Get platform + * + * @return bool|mixed + */ + public function platform() + { + return Platform::get($this); + } + + /** + * Get firmware version + * + * @return bool|mixed + */ + public function fmVersion() + { + return Platform::getVersion($this); + } + + /** + * Get work code + * + * @return bool|mixed + */ + public function workCode() + { + return WorkCode::get($this); + } + + /** + * Get SSR + * + * @return bool|mixed + */ + public function ssr() + { + return Ssr::get($this); + } + + /** + * Get pin width + * + * @return bool|mixed + */ + public function pinWidth() + { + return Pin::width($this); + } + + /** + * @return bool|mixed + */ + public function faceFunctionOn() + { + return Face::on($this); + } + + /** + * Get device serial number + * + * @return bool|mixed + */ + public function serialNumber() + { + return SerialNumber::get($this); + } + + /** + * Get device name + * + * @return bool|mixed + */ + public function deviceName() + { + return Device::name($this); + } + + /** + * Disable device + * + * @return bool|mixed + */ + public function disableDevice() + { + return Device::disable($this); + } + + /** + * Enable device + * + * @return bool|mixed + */ + public function enableDevice() + { + return Device::enable($this); + } + + /** + * Get users data + * + * @return array [userid, name, cardno, uid, role, password] + */ + public function getUser() + { + return User::get($this); + } + + /** + * Set user data + * + * @param int $uid Unique ID (max 65535) + * @param int|string $userid ID in DB (same like $uid, max length = 9, only numbers - depends device setting) + * @param string $name (max length = 24) + * @param int|string $password (max length = 8, only numbers - depends device setting) + * @param int $role Default Util::LEVEL_USER + * @param int $cardno Default 0 (max length = 10, only numbers) + * @return bool|mixed + */ + public function setUser($uid, $userid, $name, $password, $role = Util::LEVEL_USER, $cardno = 0) + { + return User::set($this, $uid, $userid, $name, $password, $role, $cardno); + } + + + + /** + * Remove All users + * + * @return bool|mixed + */ + public function clearUsers() + { + return User::clear($this); + } + + /** + * Remove admin + * + * @return bool|mixed + */ + public function clearAdmin() + { + return User::clearAdmin($this); + } + + /** + * Remove user by UID + * + * @param integer $uid + * @return bool|mixed + */ + public function removeUser($uid) + { + return User::remove($this, $uid); + } + + + + /** + * Get fingerprint data array by UID + * TODO: Can get data, but don't know how to parse the data. Need more documentation about it... + * + * @param integer $uid Unique ID (max 65535) + * @return array Binary fingerprint data array (where key is finger ID (0-9)) + */ + public function getFingerprint($uid) + { + return Fingerprint::get($this, $uid); + } + + /** + * Set fingerprint data array + * TODO: Still can not set fingerprint. Need more documentation about it... + * + * @param integer $uid Unique ID (max 65535) + * @param array $data Binary fingerprint data array (where key is finger ID (0-9) same like returned array from 'getFingerprint' method) + * @return int Count of added fingerprints + */ + public function setFingerprint($uid, array $data) + { + return Fingerprint::set($this, $uid, $data); + } + + /** + * Remove fingerprint by UID and fingers ID array + * + * @param integer $uid Unique ID (max 65535) + * @param array $data Fingers ID array (0-9) + * @return int Count of deleted fingerprints + */ + public function removeFingerprint($uid, array $data) + { + return Fingerprint::remove($this, $uid, $data); + } + + + /** + * Get attendance log + * + * @return array [uid, id, state, timestamp] + */ + public function getAttendance() + { + return Attendance::get($this); + } + + /** + * Clear attendance log + * + * @return bool|mixed + */ + public function clearAttendance() + { + return Attendance::clear($this); + } + + /** + * Set device time + * + * @param string $t Format: "Y-m-d H:i:s" + * @return bool|mixed + */ + public function setTime($t) + { + return Time::set($this, $t); + } + + /** + * Get device time + * + * @return bool|mixed Format: "Y-m-d H:i:s" + */ + public function getTime() + { + return Time::get($this); + } + + + /** + * turn off the device + * + * @return bool|mixed + */ + public function shutdown() + { + return Device::powerOff($this); + } + + /** + * restart the device + * + * @return bool|mixed + */ + public function restart() + { + return Device::restart($this); + } + + + /** + * make sleep mood the device + * + * @return bool|mixed + */ + public function sleep() + { + return Device::sleep($this); + } + + + /** + * resume the device from sleep + * + * @return bool|mixed + */ + public function resume() + { + return Device::resume($this); + } + + + /** + * voice test Sound will "Thank you" + * + * @return bool|mixed + */ + public function testVoice() + { + return Device::testVoice($this); + } + + + public function clearLCD() + { + return Device::clearLCD($this); + } + + + public function writeLCD() + { + return Device::writeLCD($this, 2, "RAIHAN Afroz Topu"); + } +} \ No newline at end of file