diff --git a/lib/chip/state b/lib/chip/state deleted file mode 100755 index 7457f40..0000000 --- a/lib/chip/state +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -/usr/bin/env php $(realpath $(dirname $0))/source/state.php $*; diff --git a/lib/global-state/set b/lib/global-state/set new file mode 100755 index 0000000..d27aec8 --- /dev/null +++ b/lib/global-state/set @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/bin/env php $(realpath $(dirname $0))/source/set.php $*; diff --git a/lib/chip/source/state.php b/lib/global-state/source/set.php similarity index 86% rename from lib/chip/source/state.php rename to lib/global-state/source/set.php index d8dc0ae..457c706 100755 --- a/lib/chip/source/state.php +++ b/lib/global-state/source/set.php @@ -5,7 +5,7 @@ require_once __DIR__.'/../../include/php/const'; - function chip_state($p_id_chip, $p_state){ + function globalstate_set($p_id_chip, $p_state){ $g_pins = null; $g_state = null; @@ -50,8 +50,10 @@ } /* (3) If chip not found -> abort */ - if( is_null($g_pins) || is_null($g_state) ) + if( is_null($g_pins) || is_null($g_state) ){ + slog("Chip $p_id_chip not found", 'global-state:set'); return 127; + } @@ -61,8 +63,10 @@ foreach($g_pins as $pinNumber) // if failed -> propagate error - if( syscall(SOURCE_DIR."/lib/gpio/out {$pinNumber}") == false ) + if( syscall(SOURCE_DIR."/lib/gpio/out {$pinNumber}") == false ){ + slog("Chip $p_id_chip cannot be set to OUT mode", 'global-state:set'); return 127; + } /* (2) For each pin, set the associated value */ foreach($g_state as $pinIndex=>$pinValue){ @@ -90,7 +94,7 @@ /* [2] Launch main script =========================================================*/ - $exec = chip_state($argv[1], $argv[2]); + $exec = globalstate_set($argv[1], $argv[2]); echo $exec; die($exec); diff --git a/lib/global-state/source/update.php b/lib/global-state/source/update.php new file mode 100755 index 0000000..fb588b4 --- /dev/null +++ b/lib/global-state/source/update.php @@ -0,0 +1,76 @@ +#!/usr/bin/php + +eof() ){ + + // {1} Try to parse current line // + $parsed = json_decode($f_chips->fgets(), true); + + // {2} If cannot parse, go to next // + if( is_null($parsed) ) + continue; + + // {3} Check if position available in GSTATE // + if( isset($gstate[$parsed[0]]) ){ + + /* (1) If according state does not exist -> go to next chip */ + if( !isset($parsed[2][$gstate[$parsed[0]]) ) + break; + + /* (2) Use more human-readable data */ + $position = $parsed[0]; + $state = $gstate[$parsed[0]]; + + /* (3) Set state */ + $updated = syscall(SOURCE_DIR."/lib/global-state/set {$position} {$state}"); + + /* (4) If error */ + if( $updated === false ) + slog("Cannot set ${position} chip to state {$state}", 'global-state:update'); + + } + } + return 0; + + } + + + /* [3] Launch main script + =========================================================*/ + $exec = globalstate_update(); + + echo $exec; + die($exec); +?> + diff --git a/lib/global-state/update b/lib/global-state/update new file mode 100755 index 0000000..0cb51e3 --- /dev/null +++ b/lib/global-state/update @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/bin/env php $(realpath $(dirname $0))/source/update.php; diff --git a/lib/mfrc522/source/.loop.php.swp b/lib/mfrc522/source/.loop.php.swp new file mode 100644 index 0000000..8bdfea5 Binary files /dev/null and b/lib/mfrc522/source/.loop.php.swp differ diff --git a/lib/mfrc522/source/loop.php b/lib/mfrc522/source/loop.php index 74315ed..03f4776 100755 --- a/lib/mfrc522/source/loop.php +++ b/lib/mfrc522/source/loop.php @@ -4,28 +4,302 @@ require_once __DIR__.'/../../include/php/const'; - $f_authlist; + $f_auth; $f_accesslog; + $f_states; + $f_actions; + + $state = []; + + $actions = []; + $states = []; + + $last_user = null; + $timeout = 0; + + + + + + + + /* RETURNS THE PERMISSIONS FOR A SPECIFIC CODE AND AN ACTION + * + * @code 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; + + } + + + + + + + + + + - $last_card; - $times; 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 =========================================================*/ - $f_authlist = new SplFileObject(AUTH_LIST, 'r'); + /* (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 @authlist to cache [action]=>[rfid1, rfid2, ..] - =========================================================*/ - - /* [3] Initialize global variables + + /* [2] Parse ACTIONS and cache them =========================================================*/ - $last_card = null; - $times = 0; + /* (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; + } @@ -35,28 +309,91 @@ 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 =========================================================*/ - while( ($code=syscall(SOURCE_DIR.'/lib/mfrc522/read')) === false ); + /* (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'); - $start_ts = microtime(true); /* [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']; - - /* [3] Manage action (states) - =========================================================*/ - - - /* [n] Wait for 0.5 s before reading again - =========================================================*/ - while( microtime(true)-$start_ts < 0.5 ); } @@ -64,9 +401,21 @@ + + + + + slog('daemon started (loop)', 'mfrc522:loop'); - while( true ) + while( true ){ + + $start_ts = microtime(true); + mfrc522_loop(); + + while( microtime(true) - $start_ts < 0.5 ); + + } ?>