#!/usr/bin/php Some RFID code * * @return user User info if found ; else NULL * */ function auth($code){ global $f_auth; /* (1) Goto first line */ $f_auth->seek(0); /* (2) Parse each line */ while( $f_auth->eof() ){ /* (3) Try to parse line */ $parsed = json_decode($f_auth->fgets(), true); /* (3) Ignore if parse error */ if( is_null($parsed) ) continue; /* (4) If wrong code -> go to next */ if( $parsed[0] != $code ) continue; /* (5) return user info */ return [ 'id' => $parsed[1], 'can' => $parsed[2] ]; } /* (6) Return FALSE if not found */ return null; } /* PROCESS ACTIONS IF ACTION CAN BE PERFORMED BY USER (DEPENDING ON TIMEOUT/STATE) * * @user Current user information * * @return success If success (action performed) | FALSE * */ function act($user){ /* [1] Export global caches + variables =========================================================*/ /* (1) Variables */ global $timeout; /* (2) Caches */ global $actions, $states, $state; /* (3) Log history file descriptor */ global $f_accesslog; /* [2] Manage timeout =========================================================*/ /* (1) If no action for this @timeout -> reset to 0 */ if( !isset($actions[$timeout]) || !is_array($actions[$timeout]) ) $timeout = 0; /* (2) fetch actions for the current @timeout */ $actionlist = $actions[$timeout]; /* [3] Manage permissions (action that can be performed) =========================================================*/ /* (1) Will contain available @id_action-s granted for the user */ $grantedFor = []; /* (2) Search an action the user can perform in the list */ foreach($actionlist as $id_action=>$acttion){ /* (3) If have permission -> add to list */ if( in_array($id_action, $user['can']) ) $grantedFor[] = $id_action; } /* (4) If no action found -> abort */ if( count($grantedFor) == 0 ) return false; /* [4] Manage action depending on the actual STATE =========================================================*/ /* (1) Will contain the first available action depending on STATE */ $toPerform = null; /* (2) For each granted action, check the STATE */ foreach($grantedFor as $id_action){ $failed = false; /* (3) Fetch action data */ $action = $actionlist[$id_action]; /* (4) Check if the state allows the action to be performed */ for( $c = 0 ; $c < count($state) || $c < strlen($action['prev']) ; $c++ ){ // {1} 'X' = any state -> so ignore // if( $action['prev'][$c] == 'X' ) continue; // {2} Other state : if not equal -> set as FAILED // else if( $action['prev'][$c] != $state[$c] ){ $failed = true; break; } } /* (5) If not failed -> save action in @toPerform */ if( !$failed ){ $toPerform = $id_action; break; } } /* (6) If no action cant be performed -> abort */ if( is_null($toPerform) ) return false; /* (7) Extract corresponding action */ $action = $grantedFor[$toPerform]; /* [5] Process the action on the STATE =========================================================*/ /* (1) Update the state with the found action */ for( $c = 0 ; $c < count($state) || $c < strlen($action['next']) ; $c++ ){ // {1} If 'x' -> let the current state // if( $action['next'][$c] == 'X' ) continue; // {2} If other state -> update it // else $state[$c] = $action['next'][$c]; } /* (2) Update the state file */ $written = @file_put_contents(STATE_CONF, implode('', $state)); /* (3) Manage error */ if( $written === false ) slog('cannot update STATE file', 'mfrc522:loop'); /* [6] Log action =========================================================*/ /* (1) Log action to default log file */ $f_accesslog->fwrite( json_encode([ time(), $user['id'], $toPerform ]).PHP_EOL ); /* (2) Return status */ return true; } function mfrc522_setup(){ /* [0] Initialize global variables =========================================================*/ /* (1) File descriptiors */ global $f_auth, $f_accesslog; /* (2) Caches */ global $actions, $states, $state; /* [1] Open file descriptors on useful files =========================================================*/ /* (1) Read accesses */ $f_auth = new SplFileObject(AUTH_LIST, 'r'); $f_states = new SplFileObject(STATES_CONF, 'r'); $f_actions = new SplFileObject(ACTIONS_CONF, 'r'); $f_gstate = @file_get_contents(STATE_CONF); /* (2) Append accesses (logs) */ $f_accesslog = new SplFileObject(ACCESS_LOG, 'a'); /* [2] Parse ACTIONS and cache them =========================================================*/ /* (1) Parse each line */ while( $f_actions->eof() ){ /* (2) Try to parse line */ $parsed = json_decode($f_actions->fgets(), true); /* (2) Ignore if parse error */ if( is_null($parsed) ) continue; /* (3) Add key (timeout) to cache */ if( !isset($actions[$parsed[0]] ) $actions[$parsed[0]] = []; /* (4) Add entry to cache */ $actions[$parsed[0]][$parsed[1]] = [ 'prev' => $parsed[2], 'next' => $parsed[3] ]; } /* (5) Free file descriptor */ $f_actions = null; /* [3] Parse STATES and cache them =========================================================*/ /* (1) Parse each line */ while( $f_states->eof() ){ /* (2) Try to parse line */ $parsed = json_decode($f_states->fgets(), true); /* (2) Ignore if parse error */ if( is_null($parsed) ) continue; /* (3) Add entry to cache */ $states[] = $parsed; } /* (4) Free file descriptor */ $f_states = null; /* [4] Cache global state =========================================================*/ /* (1) Check file */ if( $f_gstate === false ) return; /* (2) Remove surrounding spaces */ $f_gstate = preg_replace('@^\s+@', '', $f_gstate); $f_gstate = preg_replace('@\s+$@', '', $f_gstate); /* (3) For each character create an entry */ for( $c = 0 ; $c < strlen($f_gstate) ; $c++ ) $state[] = $c; } function mfrc522_loop(){ /* [1] Load global variables =========================================================*/ /* (1) Persistent variabes */ global $last_user; global $timeout; /* (2) Caches */ global $actions, $states, $state; /* [1] Wait for rfid card =========================================================*/ /* (1) Read card */ $code = syscall(SOURCE_DIR.'/lib/mfrc522/read')); /* (2) If no card read -> reset @last_user / @timeout + abort */ if( $code === false ){ $last_user = null; $timeout = 0; return false; } /* (3) If code -> format it */ $code = strtoupper($code); slog("card '$code' read", 'mfrc522:read'); /* [2] Check for user in auth list =========================================================*/ /* (1) Check user for this code */ $user = auth($code); /* (2) If not found -> reset @last_user / @timeout + abort */ if( is_null($user) ){ $last_user = null; $timeout = 0; return false; } /* [3] Manage @timeout incrementation + @last_user =========================================================*/ /* (1) If same as last -> increment @timeout */ if( $last_user == $user['id'] ) $timeout++; /* (2) If different -> reset @timeout to 0 */ else $timeout = 0; /* [4] Manage action =========================================================*/ /* (1) Try to process action */ $performed = act($user); /* (2) If performed -> update chip according to new state */ if( $performed ){ // {1} Try to update state // $updateds = syscall(SOURCE_DIR.'/lib/global-state/update'); // {2} If not updated -> error // if( !$updateds ) slog("Cannot update chips to '".implode('',$state)."'", 'mfrc522:loop'); // {3} If updated -> success // else slog("Chips updated to ".implode('',$state)."'", 'mfrc522:loop'); /* (3) If not performed -> log error */ }else slog("Cannot perform action by '$code' at timeout $timeout", 'mfrc522:loop'); /* (4) Store user for next loop */ $last_user = $user['id']; } slog('daemon started (loop)', 'mfrc522:loop'); while( true ){ $start_ts = microtime(true); mfrc522_loop(); while( microtime(true) - $start_ts < 0.5 ); } ?>