# Демон отвечающий за общение с ридерами посредством серийного порта.
# Он занимается исключительно ридерами и ничем больше.
#
# Зап
#
# crdsrvz.pl [-d] [-l] [<ttyS>], где:
#
# -d стартовать как демон
# -l logged into syslogd
# <ttyS> устройство серийного порта обозначенного в /dev. По умолчанию: ttyS1 (COM2)
require 'getopts.pl';
Getopts ('dl');
# Our program name
( my $program = $0 ) =~ s/^.*\///;
# Стартуем как демон, если указан ключик -d
if ($opt_d) {
$pid = fork();
die "can't fork: $!" unless defined $pid;
exit 0 if ($pid);
}
# use strict;
use Socket;
use Fcntl qw ( F_GETFL F_SETFL O_NONBLOCK );
use Device::SerialPort qw( :PARAM :STAT 0.07 );
use Time::HiRes qw( sleep usleep gettimeofday tv_interval );
use POSIX;
use Class::Struct qw( struct );
use Symbol;
use Sys::Syslog;
use vars qw( $PortObj @readers );
# Local vars
my $port = 'ttyS1';
my $baud = 1200;
my $parity = 'none';
my $facility = 'local1';
my $maxlen = 11; # Из сокета
my $start_tx = 0x02; my $end_tx = 0x03;
my $readers = 7;
my $quitphrase = "quit";
my $commandtimeout = 10;
# my $delaycycle = 100000;
my $delaycycle = 0.001;
my $skipcycles = 8;
my $red = 0x2;
my $green = 0x4;
my $soundalarm = $red + $green;
my $unlock = 0x8;
my $command_unlock = "1";
my $command_lock = "2";
my $command_spy = "3";
my $command_alarm = "4";
my $command_detect = "5";
my %msgs = (
detect => "Detect reader",
establ => "Connection established",
stconn => "Starting wait connect",
stcard => "Starting wait cards",
opdoor => "Door is opened on reader",
cldoor => "Door is closed on reader",
intsig => "Got interrupted signal",
slwarn => "WARNING! usleep not defined",
busy => "Reader still busy",
overbusy => "Reader busy time is over",
stop => "Reader stopping person",
alrm => "Detect strange card"
);
# Programs vars
$port = $ARGV[0] unless ($#ARGV < 0);
my $cardsock = "/tmp/card.$port";
my $lockfile = "/var/lock/LCK..$port";
struct Reader => {
addr => '$', # Адрес ридера
online => '$', # Подключен-ли физически
broke => '$', # Авария
card => '$', # Прочитанная карточка
busy => '$', # Занят - пропускаем
blink8 => '$', # Сигнал открытия замка
blink4 => '$', # Состояние лампочки green
blink2 => '$', # Состояние лампочки red
green => '$', # Можно проходить
red => '$', # Нельзя проходить
alrm => '$', # Сработал датчик
command => '$', # Поступившая комманда
timer => '$' # Таймер после последней комманды, карточки и т.п.
};
# Redefine __DIE__
sub die_quit {
$warnmessage = shift;
syslog_report ($warnmessage);
# Part ...: Close UNIX socket
# close ($server);
close (SERVER);
#
unlink $cardsock if (-e $cardsock);
# Part ...: Close serial device
readyall_turn(0);
#close ($PortObj);
# $PortObj->close if (defined $PortObj);
undef $PortObj if (defined $PortObj);
exit;
}
# Redifine QUIT
sub QUIT {
die_quit $msgs{intsig};
}
# Redefine interupt
$SIG{INT} = \&QUIT;
$SIG{HUP} = \&QUIT;
$SIG{__DIE__} = 'die_quit';
syslog_report ($msgs{slwarn}) unless defined &Time::HiRes::usleep;
# Init device
unlink $lockfile;
$PortObj = Device::SerialPort->new ("/dev/$port", TIOSDTR, $lockfile);
die "Can't open serial port $port: $!" unless (defined $PortObj);
$PortObj->read_const_time (10000);
$PortObj->baudrate ($baud);
$PortObj->parity ($parity);
$PortObj->databits (7);
$PortObj->handshake ('rst');
$PortObj->are_match (pack ("C", $end_tx));
# $PortObj->dtr_active(1);
# Start UNIX socket
socket (SERVER, PF_UNIX, SOCK_STREAM, 0);
# unlink $cardsock;
bind (SERVER, sockaddr_un ($cardsock)) or die "Can't create server: $!";
listen (SERVER, SOMAXCONN) or die "Couldn't listen on socket: $!";
# Allocate $readers listen servers;
@readers = ();
for ($id = 0; $id <= $readers; $id++) {
$readers[$id] = Reader->new();
$readers[$id]->addr($id * 0x10);
$readers[$id]->online(0);
$readers[$id]->blink8(0);
$readers[$id]->blink4(0);
$readers[$id]->blink2(0);
$readers[$id]->command(0);
$readers[$id]->broke(0);
($result) = ready_turn ($readers[$id], 0);
if (defined $result) {
syslog_report ("$id: " . $msgs{detect});
$readers[$id]->online (1);
}
}
# Система не обслуживается
# $readers[0]->online(0);
busyall_turn (1);
#############################################################################
# Primary cicle
#############################################################################
syslog_report ($msgs{stconn});
while (accept (CLIENT, SERVER)) {
# Взводим в исходное состояние все лазеры и все датчики
busyall_turn (0); resetall_turn (1);
syslog_report ($msgs{establ});
syslog_report ($msgs{stcard});
# Делаем сокет неблокирующим
Socket_Block (0);
my $gotit = ""; my $time0; my $elapsed;
while ($gotit ne $quitphrase) {
foreach $reader (@readers) {
# Проверяем - подключен ли ридер
next unless ($reader->online);
$id = $reader->addr / 16;
# Если он в аварийном состоянии
if ($reader->broke) {
($result) = broke_turn ($reader);
if ($result) {
$reader->broke(0);
}
next;
}
# Проверяем комманду и пытаемся ее выполнить
if ($reader->command) {
syslog_report ("$id: Command is " . $reader->command);
execute_command ($reader);
}
# Если пытаются пройти по "левой карточке"
if ($reader->alrm) {
alrm_turn ($reader);
$time0 = [gettimeofday]; $elapsed = tv_interval ($reader->timer, $time0);
$reader->alrm (0) if ($elapsed > $commandtimeout);
syslog_report ("$id: " . $msgs{alrm} . " left $elapsed");
next;
}
# Если ридер кого-то пропускает, то игнорируем датчик либо ждем срабатывания датчика,
if (my $times=$reader->green) {
unlock_door ($reader, 1) if ($times == $skipcycles);
if ($times > 1) {
$result = unlock_door ($reader, 0);
$times = (result) ? $times - 1 : 1;
$reader->green($times);
} else {
($result) = lock_door ($reader);
# Если датчик сработал, то взводимся в исходное состояние
if ($result) {
$reader->green(0);
syslog_report ("$id: " . $msgs{cldoor});
}
}
next;
}
# Если нельзя проходить ?
if ($reader->red) {
($result) = busy_turn ($reader, 1);
$time0 = [gettimeofday]; $elapsed = tv_interval ($reader->timer, $time0);
$reader->red(0) if ($elapsed > $commandtimeout);
syslog_report ("$id: " . $msgs{stop} . " left $elapsed");
# if ($result) {
# $reader->red (0); $reader->command (3);
# }
next;
}
my $card = "";
# Если ридер занят
if ($reader->busy) {
$time0 = [gettimeofday]; $elapsed = tv_interval ($reader->timer, $time0);
syslog_report ("$id: " . $msgs{busy} . " left $elapsed");
# Если истекло время занятости - вызываем бригаду ремонтников
if ($elapsed <= $commandtimeout) {
($result) = busy_turn ($reader);
} else {
syslog_report ("$id: " . $msgs{overbusy});
$reader->busy(0);
# $reader->broke(1);
}
next;
}
# Posle vseh proverok
# ($result, $card) = ready_turn ($reader, 1);
($result, $card) = ready_turn ($reader);
# Врубаем звуковой сигнал, если сработал датчик
unless ($result) {
$reader->command ($command_detect);
syslog_report ("$id: " . $msgs{opdoor});
next;
}
next unless (defined $card);
next if ($card eq "");
$card = $reader->card;
$time0 = [gettimeofday];
$reader->timer($time0);
$reader->busy (1);
# Пукнули для того, чтобы сказать, что карточка прочитана
# cardreaded_turn ($reader, 1);
my $msg = "Received $card(". length ($card) . ")";
syslog_report ("$id: " . $msg);
defined syswrite (CLIENT, "$id $card\n", $maxlen) or die "Can't send data: $!";
# Если увидели карточку - перестаем моргать
ready_turn ($reader, 0);
} # foreach $reader (@readers)
# Ждем либо карточку, либо команду с клиента
$when = sysread (CLIENT, $gotit, $maxlen);
if (defined $when) {
next if ($gotit eq $quitphrase);
# syslog_report ("Received answer: $gotit");
($id, $command) = split " ", $gotit;
$readers[$id]->command ($command);
} # if (defined $when)
# print "...Sleeping $delaycycle sec...\n";
# sleep ($delaycycle);
} # while ($gotit ne "quit")
# Тушим все лампочки и обнуляем все сигналы
foreach $reader (@readers) {
if ($reader->online) {
($result) = ready_turn ($reader, 0);
$reader->online (1) if (defined $result);
} else { next; }
$reader->busy (0); ready_turn ($reader, 0);
$reader->red(0); $reader->green (0); $reader->alrm (0); $reader->broke (0);
$reader->card ("");
}
busyall_turn (1);
# Делаем сокет блокирующим
Socket_Block (1);
syslog_report ($msgs{stconn});
}
exit 0;
#############################################################################
# All subroutines for this program
#############################################################################
sub execute_command {
local $reader = shift;
my $command = $reader->command;
my $time0 = [gettimeofday];
$reader->timer($time0);
SWITCH: {
# GREEN SIGNAL
if ($command eq $command_unlock) {
$reader->busy(0);
$reader->green($skipcycles);
last SWITCH;
}
# RED SIGNAL
if ($command eq $command_lock) {
$reader->busy(0);
$reader->red(1);
last SWITCH;
}
# SPY SIGNAL
if ($command eq $command_spy) {
$reader->busy(0);
$reader->red(1);
last SWITCH;
}
# ALARM SIGNAL
if ($command eq $command_alarm) {
$reader->busy(0);
$reader->green(0);
$reader->red(0);
$reader->alrm(1);
$reader->blink2(1);
$reader->blink4(0);
last SWITCH;
}
# DETECT SIGNAL
if ($command eq $command_detect) {
$reader->busy(0);
$reader->green(1);
last SWITCH;
}
}
$reader->command(0);
$reader->card("");
}
sub syslog_report {
my @str = @_;
if ($opt_l) {
# print "...logging into syslog...\n";
openlog($program, 'pid', $facility);
syslog ('info', "@str ");
closelog;
}
unless ($opt_d) {
$timestr = strftime "%Y.%m.%d %H:%M:%S", localtime();
print "$timestr @str \n";
}
}
sub Buffer_Clear {
my $verbose=shift;
$verbose=1 unless defined $verbose;
$PortObj->lookclear; $PortObj->input;
syslog_report ("ERROR: received illegal symbols") if $verbose;
}
sub waitfor {
my $timeout = $PortObj->get_tick_count + (1000 * shift);
# Ощищаем буффер приема
$PortObj->lookclear;
my $gotit = ""; my $found; my $end;
# Ждем ответ или вываливаемся по таймауту
for (;;) {
$gotit = $PortObj->lookfor;
return unless defined $gotit;
if ($gotit ne "") {
($found, $end) = $PortObj->lastlook;
return $gotit.$found;
}
return if ($PortObj->reset_error);
return if ($PortObj->get_tick_count > $timeout);
} # for
} # sub waitfor
sub alrm_turn {
local $reader = shift;
my $turn;
$turn1 = ($reader->blink2 == 1) ? 0 : 1;
$turn2 = ($reader->blink4 == 1) ? 0 : 1;
local $askreq = pack ("C", $reader->addr + $turn1 * $red + $turn2 * $green);
($status) = portexchange ($askreq);
$reader->blink2($turn1);
$reader->blink4($turn2);
return ($status);
}
sub broke_turn {
local $reader = shift;
my $turn = shift;
$turn = ($reader->blink4 == 1) ? 0 : 1 unless defined $turn;
local $askreq = pack ("C", $reader->addr + $turn * $soundalarm);
($status) = portexchange ($askreq);
$reader->blink4($turn);
$reader->blink2($turn);
return ($status);
}
sub reset_turn {
local $reader = shift;
my $turn = shift;
$turn = ($reader->blink2 == 1) ? 0 : 1 unless defined $turn;
local $askreq = pack ("C", $reader->addr + $turn * $unlock);
($status) = portexchange ($askreq);
$reader->blink8($turn);
return ($status);
}
sub busy_turn {
local $reader = shift;
my $turn = shift;
$turn = ($reader->blink2 == 1) ? 0 : 1 unless defined $turn;
local $askreq = pack ("C", $reader->addr + $turn * ($red));
($status) = portexchange ($askreq);
$reader->blink2($turn);
return ($status);
}
sub unlock_door {
local $reader = shift;
my $sound = shift;
$sound = 0 unless defined $sound;
my $turn = ($sound) ? $unlock + $soundalarm : $unlock + $green;
# Включаем
local $askreq = pack ("C", $reader->addr + $turn);
($status) = portexchange ($askreq);
$reader->blink8(1);
$reader->blink4($sound);
$reader->blink4(1);
return ($status);
}
sub lock_door {
local $reader = shift;
# Включаем
local $askreq = pack ("C", $reader->addr + $green);
($status) = portexchange ($askreq);
$reader->blink8(0);
return ($status);
}
sub cardreaded_turn {
local $reader = shift;
my $turn = shift;
$turn = ($reader->blink8 == 1) ? 0 : 1 unless defined $turn;
local $askreq = pack ("C", $reader->addr + $turn * ($red + $green));
($status) = portexchange ($askreq);
local $askreq = pack ("C", $reader->addr + $turn * ($red));
($status) = portexchange ($askreq);
$reader->blink2($turn);
return ($status);
}
sub ready_turn {
local $reader = shift;
my $turn = shift;
my $card = "";
$turn = ($reader->blink4 == 1) ? 0 : 1 unless defined $turn;
# Переключаем зеленую лампочку на ридере
local $askreq = pack ("C", $reader->addr + $turn * $green);
($status, $card) = portexchange ($askreq);
# Если оба undef - ридер не отвечает
return unless (defined ($card) and defined ($status));
$reader->blink4($turn);
# Если 8 пробелов - пропускаем
return ($status, "") if ($card =~ /\s{8}/);
$reader->card($card);
return ($status, $card);
}
sub portexchange {
my $askreq = shift;
my $status = 0;
my $verbose = 1;
local $card = "";
$PortObj->write ($askreq);
sleep ($delaycycle);
# read serial port
my $inport = waitfor (1);
# Проверяем состояние ридера на предмет наличия карточки
return unless defined $inport;
# Если $in < 0xb - у нас проблемы
unless (length ($inport) == 12) { Buffer_Clear($verbose); return ($status); }
# Получили каких-то 12 байт
# Выдираем номер карточки и все служебные символы
my @result = unpack ("C*", $inport);
$echocomm = $result[0]; $stx = $result[1]; $etx = $result[11];
unless ($echocomm == unpack ("C", $askreq)) { Buffer_Clear($verbose); return ($status); }
unless (($stx == $start_tx) and ($etx == $end_tx)) { Buffer_Clear($verbose); return ($status); }
$status = ($result[10] & 8) / 8;
$card = pack ("C*", @result [2..9]);
return (! $status, $card);
}
sub readyall_turn {
my $turn = shift;
local $reader;
foreach $reader (@readers) {
ready_turn ($reader, $turn) if $reader->online;
}
}
sub busyall_turn {
my $turn = shift;
local $reader;
foreach $reader (@readers) {
busy_turn ($reader, $turn) if ($reader->online);
}
}
sub resetall_turn {
my $turn = shift;
local $reader;
foreach $reader (@readers) {
reset_turn ($reader, $turn) if ($reader->online);
}
}
sub Socket_Block {
# Если $1 = 0 то сокет неблокирующий, иначе блокируется
my $block = shift;
my $flags = fcntl (CLIENT, F_GETFL, 0) or die "Can't get flags for the socket: $!";
$flags = ($block) ? ($flags | O_NONBLOCK) : ($flags + O_NONBLOCK);
$flags = fcntl (CLIENT, F_SETFL, $flags) or die "Can't set flags for the socket: $!";
return $flags;
}
Комментариев нет:
Отправить комментарий