SATS/feature/rfid-read/source/loop.php

480 lines
9.8 KiB
PHP
Raw Normal View History

2017-01-30 10:41:25 +00:00
#!/usr/bin/php
<?php
2017-09-26 15:53:58 +00:00
require_once __DIR__.'/../../../lib/include/php/const';
2017-01-30 10:41:25 +00:00
$f_auth;
2017-01-30 10:41:25 +00:00
$f_accesslog;
$f_actions;
$actions = [];
$last_user = null;
$timeout = 0;
$FEATURE = basename(dirname(__DIR__));
2017-02-23 15:11:52 +00:00
/* RETURN CURRENT GLOBAL STATE
*
* @return gstate<string> Current global state as string
*
*/
function get_gstate(){
2017-02-23 15:53:28 +00:00
/* (1) Initialize file descriptor */
$f_gstate = new SplFileObject(STATE_CONF, 'r');
2017-02-23 15:11:52 +00:00
/* (2) Read first line */
$f_gstate->seek(0);
2017-02-23 15:37:03 +00:00
$state = $f_gstate->current();
2017-02-23 15:11:52 +00:00
$state = preg_replace('@^\s+@', '', $state);
$state = preg_replace('@\s+$@', '', $state);
2017-02-23 15:53:28 +00:00
/* (3) Free file descriptor */
/* (4) Check data */
2017-02-23 15:11:52 +00:00
if( strlen($state) < 1 )
return false;
2017-02-23 15:53:28 +00:00
/* (5) Return global state */
2017-02-23 15:11:52 +00:00
return $state;
}
/* RETURNS THE PERMISSIONS FOR A SPECIFIC CODE AND AN ACTION
*
* @code<string> Some RFID code
*
* @return user<array> User info if found ; else NULL
*
*/
function auth($code){
global $f_auth;
/* (1) Goto first line */
$f_auth->seek(0);
/* (2) Parse each line */
2017-02-23 14:35:13 +00:00
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<array> Current user information
*
* @return success<bool> If success (action performed) | FALSE
*
*/
function act($user){
/* [1] Export global caches + variables
=========================================================*/
/* (1) Variables */
global $timeout;
/* (2) Caches */
2017-02-23 15:11:52 +00:00
global $actions;
/* (3) Log history file descriptor */
global $f_accesslog;
2017-02-23 15:11:52 +00:00
/* (5) Update global state */
$state = get_gstate();
2017-02-23 15:40:41 +00:00
// manage error
if( $state === false )
return false;
/* [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 */
2017-02-23 16:12:20 +00:00
foreach($actionlist as $id_action=>$action){
/* (3) If have permission -> add to list */
if( in_array($id_action, $user['can']) )
$grantedFor[] = $id_action;
}
/* (4) If no action found -> abort */
2017-02-23 15:33:35 +00:00
if( count($grantedFor) == 0 ){
2017-09-27 16:09:50 +00:00
slog('user not granted to any action', 'rfid-read:loop');
return false;
2017-02-23 15:33:35 +00:00
}
/* [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 */
2017-02-23 16:04:47 +00:00
for( $c = 0 ; $c < strlen($state) && $c < strlen($action['prev']) ; $c++ ){
2017-02-23 16:10:59 +00:00
//
// {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 */
2017-02-23 15:33:35 +00:00
if( is_null($toPerform) ){
2017-09-27 16:09:50 +00:00
slog('global state allows no action to be performed', 'rfid-read:loop');
return false;
2017-02-23 15:33:35 +00:00
}
/* (7) Extract corresponding action */
2017-02-23 16:12:20 +00:00
$action = $actionlist[$toPerform];
/* [5] Process the action on the STATE
=========================================================*/
/* (1) Update the state with the found action */
2017-02-23 16:04:47 +00:00
for( $c = 0 ; $c < strlen($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 */
2017-02-23 15:53:28 +00:00
$written = @file_put_contents(STATE_CONF, $state);
/* (3) Manage error */
if( $written === false )
2017-09-27 16:09:50 +00:00
slog('cannot update STATE file', 'rfid-read: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;
}
2017-01-30 10:41:25 +00:00
2017-02-18 10:09:49 +00:00
2017-01-30 10:41:25 +00:00
function mfrc522_setup(){
/* [0] Initialize global variables
=========================================================*/
/* (1) File descriptiors */
2017-02-23 15:53:28 +00:00
global $f_auth, $f_accesslog;
/* (2) Caches */
2017-02-23 15:11:52 +00:00
global $actions;
2017-09-26 15:53:58 +00:00
/* (3) Useful data */
global $FEATURE;
2017-01-30 10:41:25 +00:00
/* [1] Open file descriptors on useful files
=========================================================*/
/* (1) Read accesses */
2017-02-23 13:50:54 +00:00
$f_auth = new SplFileObject(AUTH_CONF, 'r');
$f_actions = new SplFileObject(ACTIONS_CONF, 'r');
/* (2) Append accesses (logs) */
2017-09-26 15:53:58 +00:00
if( !file_exists(DATA_DIR."/$FEATURE") )
file_put_contents(DATA_DIR."/$FEATURE", '');
$f_accesslog = new SplFileObject(DATA_DIR."/$FEATURE", 'a');
2017-01-30 10:41:25 +00:00
/* [2] Parse ACTIONS and cache them
2017-01-30 10:41:25 +00:00
=========================================================*/
/* (1) Parse each line */
2017-02-23 14:35:13 +00:00
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 */
2017-02-23 13:47:25 +00:00
if( !isset($actions[$parsed[0]]) )
$actions[$parsed[0]] = [];
2017-01-30 10:41:25 +00:00
/* (4) Add entry to cache */
$actions[$parsed[0]][$parsed[1]] = [
'prev' => $parsed[2],
'next' => $parsed[3]
];
}
/* (5) Free file descriptor */
$f_actions = null;
2017-02-23 15:11:52 +00:00
/* [3] Check global state
=========================================================*/
/* (1) Check file */
2017-02-23 15:40:41 +00:00
$gstate = get_gstate();
2017-02-23 15:11:52 +00:00
2017-02-23 15:40:41 +00:00
/* (2) Manage error */
if( $gstate === false )
return 127;
/* [4] Update global state
=========================================================*/
/* (1) Do it once to unlock gpio boot lag */
syscall(SOURCE_DIR.'/lib/global-state/update');
/* (2) Do it again to apply global state */
syscall(SOURCE_DIR.'/lib/global-state/update');
2017-02-23 15:40:41 +00:00
return 0;
2017-01-30 10:41:25 +00:00
}
function mfrc522_loop(){
/* [1] Load global variables
=========================================================*/
/* (1) Persistent variabes */
global $last_user;
global $timeout;
/* (2) Caches */
2017-02-23 15:11:52 +00:00
global $actions;
2017-09-27 16:09:50 +00:00
global $FEATURE;
2017-02-23 15:40:41 +00:00
/* (3) Get global state */
$gstate = get_gstate();
// Manage error
if( $gstate === false )
return false;
2017-01-30 10:41:25 +00:00
/* [1] Wait for rfid card
=========================================================*/
/* (1) Start timer */
$start_ts = microtime(true);
/* (2) Read card */
2017-09-27 16:09:50 +00:00
$code = syscall(SOURCE_DIR."/feature/$FEATURE/read");
/* (3) If no card read -> reset @last_user / @timeout + abort */
if( $code === false ){
$last_user = null;
$timeout = 0;
return false;
}
/* (4) If timeout exceeded -> reset @timeout and @last_user */
if( microtime(true) - $start_ts >= 0.5 ){
$timeout = 0;
$last_user = null;
}
/* (5) Wait for 1/2 second */
while( microtime(true) - $start_ts < 0.5 );
2017-02-18 10:09:49 +00:00
/* (6) If code -> format it */
$code = strtoupper($code);
2017-09-27 16:09:50 +00:00
slog("card '$code' read", 'rfid-read:read');
2017-01-30 10:41:25 +00:00
2017-01-30 10:41:25 +00:00
/* [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) ){
2017-09-27 16:09:50 +00:00
slog("Unknown user (not authenticated)", "rfid-read:loop");
$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);
2017-01-30 10:41:25 +00:00
/* (2) If performed -> update chip according to new state */
if( $performed ){
// {1} Try to update state //
$updateds = syscall(SOURCE_DIR.'/lib/global-state/update');
2017-01-30 10:41:25 +00:00
// {2} If not updated -> error //
if( !$updateds )
2017-09-27 16:09:50 +00:00
slog("Cannot update chips to '{$gstate}'", 'rfid-read:loop');
// {3} If updated -> success //
else
2017-09-27 16:09:50 +00:00
slog("Chips updated to '{$gstate}'", 'rfid-read:loop');
/* (3) If not performed -> log error */
}else
2017-09-27 16:09:50 +00:00
slog("Cannot perform action by '$code' at timeout $timeout", 'rfid-read:loop');
/* (4) Store user for next loop */
$last_user = $user['id'];
2017-01-30 10:41:25 +00:00
}
2017-02-23 13:47:25 +00:00
/* [1] Set up daemon
=========================================================*/
/* (1) Set up */
$exec = mfrc522_setup();
2017-02-23 13:47:25 +00:00
/* (2) Manage error */
if( $exec != 0 ){
2017-09-27 16:09:50 +00:00
slog('cannot set up the daemon', 'rfid-read:loop');
2017-02-23 13:47:25 +00:00
echo $exec;
die($exec);
}
2017-02-23 13:47:25 +00:00
/* (3) Success message */
2017-09-27 16:09:50 +00:00
slog('daemon started (loop)', 'rfid-read:loop');
2017-01-30 10:41:25 +00:00
2017-02-23 13:47:25 +00:00
/* [2] Daemon loop
=========================================================*/
while( true ){
$start_ts = microtime(true);
2017-02-18 10:09:49 +00:00
mfrc522_loop();
}
2017-01-30 10:41:25 +00:00
?>