#!/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; }