#!/usr/bin/perl -w
use strict;
use DBI;
# main variable declarations
my $this_rack = 1; # Each app runs on the node computer and needs the rack_num set.
my $raw_card_id; # id that comes straight off the card: ;1234560?
my $cleaned_id; # id which has been cleaned: 123456
my $next_dock; # the dock which will be unlocked
my $next_vehicle; # the vehicle that will be signed out
my $elapsed_minutes = 60; # number of minutes to sing out vehicles for.
print `clear`;
print "Swipe Your Hampshire ID Card\n\n";
$raw_card_id = get_user_id();
#print "\nraw_card_id: $raw_card_id\n";
# determine whether the card id is in the correct format
if(verify_user_id_format($raw_card_id))
{
#clean the id. get rid of extraneous characters (;?)
$cleaned_id = clean_user_id($raw_card_id);
#print "$clean_id is a valid id\n\n\n";
# if the user_id is present in our user database
if(look_up_user_id($cleaned_id))
{
# check to see if the user account is enabled
if(check_user_account_status($cleaned_id))
{
# check to see if the user already has a vehicle checked out
if(check_user_has_vehicle($cleaned_id)) # if already has scooter (true)
{
print "\nSorry, you already have a scooter signed out.\n";
}
else # All systems go, the user may sign out a vehicle
{
print "Finding next available vehicle...\n";
$next_dock = get_next_available_dock($this_rack);
print "Next available vehicle is in dock: $next_dock\n";
# Find the vehicle number in the dock
$next_vehicle = look_up_vehicle_num($next_dock, $this_rack);
print "\nGet vehicle ID...\n";
print "Vehicle Number: $next_vehicle\n";
# Add this event to the events table
add_events_entry("sign_out",$cleaned_id,$this_rack,$next_dock,$next_vehicle,$elapsed_minutes);
print "Sign-out added to events entry.\n";
update_dock_status($next_dock,$this_rack,"empty");
print "Update dock status. Set to empty\n";
update_vehicle_availability($next_vehicle,$cleaned_id);
print "Update vehicle availability. Set to user id: $cleaned_id\n\n";
print "Please take vehicle $next_vehicle from dock: $next_dock\n\n";
# unlock the vehicle - this needs to be called last because the unlock
# process sleeps for a while
unlock_vehicle($this_rack,$next_dock);
}
}
else
{
print "\nSorry, your account is disabled.\n";
}
}
else
{
print "\nSorry, you aren't in the database.\n";
}
}
else
{
print "Hmmm, doesn't look like that's a Hampshire ID.\n\n\n";
}
################################################################################
# Function: get_user_id
# Description: This function gets the raw id from stdin.
# Arguments: none
# Returns: string raw_id
# Notes: This function is Hampshire ID specific. The card reader
# should be set to only read track 2 which is where the id is stored.
# I need to verify that the new id's are the same.
#
# This function is also *nix specific. In order for the ID to not show
# up on the screen, `stty -echo` is used.
#
# Gotta figure out GUI stuff which may affect this and all sorts of other
# stuff
# Status: 100%
################################################################################
sub get_user_id
{
my $card_id;
# `stty -echo`;
chomp($card_id = <STDIN>);
# `stty echo`;
return $card_id;
}
################################################################################
# Function: verify_user_id_format
# Description: Verifies that the raw id is in the right format: ;2345671?
# Arguments: raw_id
# Returns: bool - true if raw id matches format
# Notes: This function is Hampshire ID specific. For some reason, it appears the
# Hampshire ID has the first character '0' stripped of the beginning and
# added to the end. Or so it appears.
# The reg exp is currently very broad, it will match any 7 numbers in a
# row.
# Status: 90% (functional needs cleanup)
################################################################################
sub verify_user_id_format
{
my $raw_id = $_[0];
my $id_validity = 0;
# print "\nraw_id: $raw_id";
# print "\n\n";
if($raw_id =~ /\;[0-9]{7}\?/)
# if($raw_id =~ /[0-9]{6}/)
{
$id_validity = "1";
}
else
{
$id_validity = "0";
}
return $id_validity;
}
################################################################################
# Function: clean_user_id
# Description: strips the identifying characters from the beginning and end of
# the id. Then puts the '0' at the end of the id back at the
# beginning (from the end).
# Arguments: raw_id
# Returns: clean id
# Notes: Hampshire ID Specific.
# Status: 90% (functional needs cleanup)
################################################################################
sub clean_user_id
{
my $raw_id = $_[0];
my $clean_id;
# match up the id for cleaning. Strip any leading zeros.
# add \; and \? for real card swipe!
$raw_id =~ /\;([0-9]{6})0\?/;
# $raw_id =~ /0([0-9]{6})/;
$clean_id = $1;
return $clean_id;
}
################################################################################
# Function: look_up_user_id
# Description: Checks to see if user is in database.
# Arguments: user_id
# Returns: bool - depending on whether the user was present.
# Status: 90% (functional needs cleanup)
################################################################################
sub look_up_user_id
{
my $user_id = $_[0];
my $in_database = 0;
my ($dsn) = "DBI:mysql:bike_program:localhost";
my ($user_name) = "bike";
my ($password) = "password";
my ($dbh, $sth);
my (@ary);
# connect to database
$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
#issue query
$sth = $dbh->prepare ("SELECT user_id FROM user WHERE user_id='$user_id'");
$sth->execute();
print "\nuserid: $user_id\n";
if(@ary = $sth->fetchrow_array())
{
$in_database = "1";
}
else
{
$in_database = "0";
}
$sth->finish();
$dbh->disconnect();
return $in_database;
}
################################################################################
# Function: check_user_account_status
# Description: Checks the user table to see if the user's account is enabled
# Arguments: user_id
# Returns: bool - true if the user account is set to enabled
# Prerequisite: This function assumes that the user exists in the database (by
# running the look_up_user_id() function.
# Status: 90% (functional needs cleanup)
################################################################################
sub check_user_account_status
{
my $user_id = $_[0];
my $account_status;
my $account_enabled;
my ($dsn) = "DBI:mysql:bike_program:localhost";
my ($user_name) = "bike";
my ($password) = "password";
my ($dbh, $sth);
my (@ary);
# connect to database
$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
#issue query
$sth = $dbh->prepare ("SELECT account_status FROM user WHERE user_id='$user_id'");
$sth->execute();
#fetch results
$account_status = $sth->fetchrow_array();
print "\naccount_status: $account_status\n";
if($account_status eq "enabled")
{
$account_enabled = "1";
}
else
{
$account_enabled = "0";
}
$sth->finish();
$dbh->disconnect();
return $account_enabled;
}
################################################################################
# Function: check_user_has_vehicle
# Description: Searches the availability field of the vehicle table to see if
# the user already has a vehicle assigned to them.
# Arguments: user_id
# Returns: bool - true if user already has vehicle (that's sort of backwards and
# confusing but I can't think of a better function name.)
# Prerequsite: This function assumes id has already gone through validity checks
# Status: 90% (functional needs cleanup)
################################################################################
sub check_user_has_vehicle
{
my $user_id = $_[0];
my $count;
my $has_scooter;
my ($dsn) = "DBI:mysql:bike_program:localhost";
my ($user_name) = "bike";
my ($password) = "password";
my ($query);
my ($dbh, $sth);
my (@ary);
# connect to database
$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
#build query
$query = "SELECT COUNT(*) FROM vehicle WHERE availability='$user_id'";
# print "\n$query\n";
#build query
$sth = $dbh->prepare($query);
$sth->execute();
#fetch results
$count = $sth->fetchrow_array();
# print "\ncount: $count\n";
if($count) # if any rows were returned
{
$has_scooter = "1"; # true: user already has a vehicle
}
else
{
$has_scooter = "0"; # false: user doesn't already have a vehicle
}
$sth->finish();
$dbh->disconnect();
# print "\nhas_scooter: $has_scooter\n\n";
return $has_scooter;
}
################################################################################
# Function: get_next_available_dock
# Description: looks at a particular rack and determines which dock to unlock.
# For example, when a vehicle is unlocked for a user, we need to
# look at the status of the docks and unlock one that has a vehicle
# in it, and ideally, one that is closest to the card swipe.
# Arguments: rack_num
# Returns: dock_num or "0" if the rack is full.
# Status: 80% (functional needs cleanup and dock selection could be a bit more
# intelligent)
# BUG!!! - Need to check if the rack is empty
################################################################################
sub get_next_available_dock
{
my $rack_num = $_[0];
my $next_dock;
my ($dsn) = "DBI:mysql:bike_program:localhost";
my ($user_name) = "bike";
my ($password) = "password";
my ($dbh, $sth);
my (@ary);
# connect to database
$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
#issue query
$sth = $dbh->prepare ("SELECT dock_num FROM dock WHERE dock_status != 'empty' ORDER BY dock_num LIMIT 1");
$sth->execute();
$next_dock = $sth->fetchrow_array();
# print "\nnext_dock: $next_dock\n";
if(!defined($next_dock)) #if there was an error or if the dock is full
{
$next_dock = "0"; #return false
# print "\ngot into not defined\n";
}
elsif($next_dock == "0E0") #if no rows were affected
{ # I don't think this ever gets used but it doesn't hurt
$next_dock = "0";
# print "\ngot into the 0E0 Statement\n";
}
$sth->finish();
$dbh->disconnect();
return $next_dock;
}
################################################################################
# Function: unlock_vehicle
# Description: Unlocks a particular lock
# Arguments: rack_num, dock_num
# Returns: bool - success or failure
# Status: 50%
################################################################################
sub unlock_vehicle
{
my $rack_num = $_[0];
my $dock_num = $_[1];
# Set the relay high (Unlock the lock)
`echo SK3 >> /dev/ttyS0`;
# Wait for the user to take the bike
`sleep 8`;
# Reset the relay (Lock the lock)
`echo RK3 >> /dev/ttyS0`;
return "1";
}
################################################################################
# Function: look_up_vehicle_num
# Description: Look in dock table at specified dock_num to determine the
# vechicle_num which is in it.
# Arguments: dock_num, rack_num
# Returns: int vehicle_num or 0 on error
# Status: 90% (functional, needs clean up)
################################################################################
sub look_up_vehicle_num
{
my $dock_num = $_[0];
my $rack_num = $_[1];
my $vehicle_num;
my $outcome;
my ($dsn) = "DBI:mysql:bike_program:localhost";
my ($user_name) = "bike";
my ($password) = "password";
my ($query);
my ($dbh, $sth);
my (@ary);
# connect to database
$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
#build query
$query = "SELECT dock_status FROM dock WHERE dock_num='$dock_num' AND rack_num='$rack_num' LIMIT 1";
# print "\n$query\n";
#build query
$sth = $dbh->prepare($query);
$sth->execute();
#fetch results
$vehicle_num = $sth->fetchrow_array();
# print "\nvehicle_num: $vehicle_num\n";
$sth->finish();
$dbh->disconnect();
if(!defined($vehicle_num)) #if there was an error
{
$outcome = "0"; #return false
}
elsif($vehicle_num eq "full")
{
$outcome = "0";
}
elsif($vehicle_num eq "empty")
{
$outcome = "0";
}
else
{
$outcome = $vehicle_num;
}
return $outcome;
}
################################################################################
# Function: add_events_entry
# Description: Gathers all the variables and makes an entry in the events table
# Arguments: action, user_id, rack_num, dock_num, vehicle_num, [elapsed_time]
# Returns:
# Status: 75% (quasi-functional need to deal with the issue of optional arguments)
################################################################################
sub add_events_entry
{
#arguments
my $action = $_[0];
my $user_id = $_[1];
my $rack_num = $_[2];
my $dock_num = $_[3];
my $vehicle_num = $_[4];
my $time_interval = $_[5];
my $rows;
my $outcome;
my ($dsn) = "DBI:mysql:bike_program:localhost";
my ($user_name) = "bike";
my ($password) = "password";
my ($query);
my ($dbh, $sth);
my (@ary);
# connect to database
$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
$query = "INSERT INTO events SET action='$action', user_id='$user_id', rack_num='$rack_num', dock_num='$dock_num', vehicle_num='$vehicle_num', time_interval='$time_interval'";
# print "\nquery: $query\n";
$rows = $dbh->do ($query);
$dbh->disconnect();
if(!defined($rows)) #if there was an error
{
$outcome = "0"; #return false
}
elsif($rows == "0E0") #if no rows were affected
{
$outcome = "0";
# print "\ngot into the 0E0 Statement\n";
}
else
{
$outcome = "1"; # return true
}
return $outcome;
}
################################################################################
# Function: update_dock_status
# Description: updates status of dock_status in the dock database
# Arguments: dock_num, rack_num, dock_status (empty or vehicle_num)
# Returns: bool - true on success
# Status: 90% (functional needs cleanup)
################################################################################
sub update_dock_status
{
my $dock_num = $_[0];
my $rack_num = $_[1];
my $dock_status = $_[2];
my $rows;
my $outcome;
my ($dsn) = "DBI:mysql:bike_program:localhost";
my ($user_name) = "bike";
my ($password) = "password";
my ($query);
my ($dbh, $sth);
my (@ary);
# connect to database
$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
$query = "UPDATE dock SET dock_status='$dock_status' WHERE rack_num='$rack_num' AND dock_num='$dock_num'";
# print "\nquery: $query\n";
$rows = $dbh->do ($query);
$dbh->disconnect();
if(!defined($rows)) #if there was an error
{
$outcome = "0"; #return false
}
elsif($rows == "0E0") #if no rows were affected
{
$outcome = "0";
# print "\ngot into the 0E0 Statement\n";
}
else
{
$outcome = "1"; # return true
}
return $outcome;
}
################################################################################
# Function: update_vehicle_availability
# Description: updates availability of specified vehicle
# Arguments: vehicle_num, availability (available, unavailable, or user_id)
# Returns: bool - 1 on success, 0 on failure
# Status: 90% (functional needs cleanup)
################################################################################
sub update_vehicle_availability
{
my $vehicle_num = $_[0];
my $availability = $_[1];
my $outcome;
my $rows;
my ($dsn) = "DBI:mysql:bike_program:localhost";
my ($user_name) = "bike";
my ($password) = "password";
my ($query);
my ($dbh, $sth);
my (@ary);
# connect to database
$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
#build query
$query = "UPDATE vehicle SET availability='$availability' WHERE vehicle_num='$vehicle_num'";
# print "\nquery: $query\n";
#run query
$rows = $dbh->do ($query);
$dbh->disconnect();
# print "\nrows: $rows\n";
if(!defined($rows)) #if there was an error
{
$outcome = "0"; #return false
# print "\nnot defined\n";
}
elsif($rows == "0E0") #if no rows were affected
{
$outcome = "0";
# print "\ngot into the 0E0 Statement\n";
}
else
{
$outcome = "1"; # return true
}
return $outcome;
}