# poslib.pl # # Poslib Perl interface for Zaza map applet. # # Copyright (C) 2001 Brian Rudy (brudyNO@SPAMpraecogito.com) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # # Requires: # Config::IniFiles # IPC::ShareLite # Math::Trig # CGI::LogCarp # # Needed by libposlib.so, DISPLAY_LOCALIZE, and posServer # ## Revision History ## # 0.54 10-19-2001 # Basic goal visit timestamp support. Fixed bug if only active goal # was 0. # # 0.53 10-12-2001 # Fixed robot heading bug in simulator translate-to-target code. # Fixed bug in simulator turn-to-target CCW, but CW turn bug remains. # # 0.52 9-12-2001 # Minor clean up for end-of-run. # # 0.51 9-8-2001 # Cleaned up debugging messages to make more Apache/Java client # friendly. Fixed y-offset for simulator mode. Turn-to-target # calulations still needs some work as does end-of-run management. # # 0.50 9-7-2001 # Added basic robot simulator for use without the robot or Beesoft. # # 0.43 8-19-2001 # Added active goal loading and comparison. Repositioned semaphore when # new voted goal is added to the active goal table. Updated getgoal() # to load multiple goals. # # 0.42 8-17-2001 # Added client timestamping and auto removal so vote tabulation only # counts current users. Added main poslib.ini reading and global # loading via loadglobals(). # # 0.41 8-16-2001 # Fleshed-out new goal semaphore and goal loading. Now operational # with basic client voting and loading to the planner. # # 0.40 8-15-2001 # Added client fingerprinting, goal voting and vote tabulation. # Added stubs for new goal semaphores. # # 0.30 8-14-2001 # Basic goal ini reading via Config::IniFiles and storage in shared # memory for posServer access. # # 0.20 8-13-2001 # Fixed position hash storage and retrieval w/ proper serialization. # # 0.10 8-10-2001 # First working version. Only impliments position retrieval. # use IPC::ShareLite; use DynaLoader; use Config::IniFiles; use Storable qw(freeze thaw); use CGI::LogCarp qw(:STDLOG carpout); my $debug = 1; my $poslibini = "/home/brudy/posServer/poslib.ini"; # Globals filled by loadglobals() my $testmode; my $goalfile; my $votingenabled; my $clientflushtimer; my @uberclients; my $mapheight; # These default to 65,536 bytes each, so use the default # with care! my $posshare = new IPC::ShareLite( -key => 1492, -create => 'yes', -destroy => 'no', -size => 200 ) or die $!; my $goalshare = new IPC::ShareLite( -key => 1493, -create => 'yes', -destroy => 'no' ) or die $!; # -destroy => 'no', # -size => 5000 ) or die $!; my $voteshare = new IPC::ShareLite( -key => 1494, -create => 'yes', -destroy => 'no' ) or die $!; my $semaphoreshare = new IPC::ShareLite( -key => 1495, -create => 'yes', -destroy => 'no', -size => 8 ) or die $!; my $clientshare = new IPC::ShareLite( -key => 1496, -create => 'yes', -destroy => 'no' ) or die $!; my $globalshare = new IPC::ShareLite( -key => 1497, -create => 'yes', -destroy => 'no', -size => 500 ) or die $!; my $activegoalshare = new IPC::ShareLite( -key => 1498, -create => 'yes', -destroy => 'no' ) or die $!; my $simshare = new IPC::ShareLite( -key => 1499, -create => 'yes', -destroy => 'no' ) or die $!; # Is the given IP an uberclient? sub isuberclient { ($ip) = @_ ; my $i; loadglobals(); for $i (0 .. $#uberclients) { if ($ip eq $uberclients[$i]) { return 1; } } return 0; } # Read the main ini file and store the results in shared memory sub readposlibini { my $poslibini = new Config::IniFiles( -file => $poslibini ); # load it all into an Hash of Hashes my %MainHoH; my $i; my @uberclients; $MainHoH{testmode} = $poslibini->val( 'CGI', 'testmode' ); $MainHoH{goalfile} = $poslibini->val( 'Goals', 'goalfile' ); $MainHoH{votingenabled} = $poslibini->val( 'Voting', 'Enabled' ); $MainHoH{clientflushtimer} = $poslibini->val( 'Voting', 'ClientTimeoutInterval' ); @uberclients = $poslibini->val('CGI', 'uberclient'); for $i (0 .. $#uberclients) { $MainHoH{uberclients}{$i} = $uberclients[$i]; } $MainHoH{visittimeout} = $poslibini->val( 'Goals', 'visittimeout' ); $globalshare->store(freeze(\%MainHoH)); #print "\%MainHoH is " . length(freeze(\%MainHoH)) . " bytes.\n"; } # Other subs can run this to load the global variables from # shared memory sub loadglobals { my $i; my %MainHoH = %{thaw($globalshare->fetch)}; $testmode = $MainHoH{testmode}; #print "\$testmode is $testmode.\n"; $goalfile = $MainHoH{goalfile}; #print "\$goalfile is $goalfile.\n"; $votingenabled = $MainHoH{votingenabled}; #print "\$votingenabled is $votingenabled.\n"; $clientflushtimer = $MainHoH{clientflushtimer}; #print "\$clientflushtimer is $clientflushtimer.\n"; for $i (keys %{ $MainHoH{uberclients} }) { $uberclients[$i] = $MainHoH{uberclients}{$i}; #print "\$uberclients[$i] is " . $MainHoH{uberclients}{$i} . ".\n"; } $mapheight = $MainHoH{mapheight}; $visittimeout = $MainHoH{visittimeout}; } # Refresh the timestamp for given client sub touchclient { ($clientip) = @_ ; # should have exclusive access for this if ($clientip ne '') { my %clienttds = %{thaw($clientshare->fetch)}; $clienttds{$clientip} = time(); $clientshare->store(freeze(\%clienttds)); } else { print "Sorry dude, you need a valid IP address.\n"; } } # check the timestamp of each client and purge the expired ones sub purgeclients { # We need exclusive access for this my $client; my $currenttime = time(); my %clienttds = %{thaw($clientshare->fetch)}; my %votelist = %{thaw($voteshare->fetch)}; my %newclienttds; my %newvotelist; my $i; loadglobals(); for $client (keys %clienttds) { if (($clienttds{$client} + $clientflushtimer) >= $currenttime) { # Save each into a temporary hash $newclienttds{$client} = $clienttds{$client}; for $i (0 .. $#{ $votelist{$client} }) { if ($votelist{$client}[$i] == 1) { $newvotelist{$client}[$i] = 1; } } } else { print "Purging " . $client . " because it hasn't voted in " . ($currenttime - $clienttds{$client}) . " seconds.\n"; } } $clientshare->store(freeze(\%newclienttds)); $voteshare->store(freeze(\%newvotelist)); # release exclusive access } # Flush simulator data sub flushsim { my %simdata; $simdata{moving} = 0; $simdata{visited}{0} = 0; $simshare ->store(freeze(\%simdata)); } # Initializes all shared memory, and loads the config info sub poslibinit { my @empty; $empty[0] = ''; readposlibini(); # not 0, helps client debugging pushpos(1, 1, 1); flushclients(); flushsemaphores(); flushvotes(); flushsim(); $activegoalshare ->store(freeze(\@empty)); read_goal_file(); logmsg "poslib initializing...\n"; } # Set the current position sub pushpos { ($x, $y, $theta) = @_ ; if (($x eq '') || ($y eq '') || ($theta eq '')) { if ($debug) { logmsg "pushpos was called with a null value!\n"; } return 0; } else { %Position = ('x' => $x, 'y' => $y, 'theta' => $theta); $posshare->store(freeze(\%Position)); # this is totally arbitrary right now return $x + $y; } } # Get the current position sub poppos { # print "Current position is x=" . %fretpos->{'x'} . ", y=" . %fretpos->{'y'} . ", theta=" . %fretpos->{'theta'} . "\n"; use Math::Trig; loadglobals(); # Simulator stuff if ($testmode) { my %simdata = %{thaw($simshare->fetch)};; my @activegoals = @{thaw($activegoalshare->fetch)}; my %currentpos = %{thaw($posshare->fetch)}; my @goalAoH = @{thaw($goalshare->fetch)}; my $currenttime = time(); if ($simdata{moving} == 0) { #if (newgoal()) { my $i; my $j = 0; my $val; my $nval; my $b = 0; # have all goals been visited? for $i (0 .. $#activegoals) { if ($simdata{visited}{$activegoals[$i]} == 1) { if ($debug) { logmsg $goalAoH[$activegoals[$i]]{Name} . " was visited " . ($currenttime - $goalAoH[$activegoals[$i]]{Timestamp}) . " seconds ago.\n"; } $j++; } else { logmsg $goalAoH[$activegoals[$i]]{Name} . " has not been visited in " . ($currenttime - $goalAoH[$activegoals[$i]]{Timestamp}) . " seconds.\n"; $b++; } } if (($j == 0) && ($b == 0)) { logmsg "Uh, oh the hash is empty!\n"; } if ($b == 0) { if ($debug) { logmsg "All goals have been visited, skipping move.\n"; } # Should we reset them? # No, this is already being done } else { # Find nearest x in activegoals if there are any goals at all if (($#activegoals == 0) && ($activegoals[0] != 0)) { if ($debug) { logmsg "There aren't any goals, can't go to one..\n"; } } else { my $nearvalid = 0; my $valid = 0; my $altval; for $i (0 .. $#activegoals) { if (($simdata{visited}{$activegoals[$i]} != 1) && ($currentpos{x} != $goalAoH[$activegoals[$i]]{Appletx})) { if (abs($currentpos{x} - $goalAoH[$activegoals[$i]]{Appletx}) < abs($currentpos{x} - $goalAoH[$activegoals[$val]]{Appletx})) { if ($debug) { logmsg abs($currentpos{x} - $activegoals[$i]) . " is less than " . abs($currentpos{x} - $activegoals[$val]) . ".\n"; logmsg "The visited flag for this goal is =" . $simdata{visited}{$activegoals[$i]} . ".\n"; } $val = $i; $nearvalid++; } else { $valid++; $altval = $i; } } } if (($nearvalid == $valid) && ($valid == 0)) { logmsg "There are no valid goals. This will cause a crash!\n"; } if ($nearvalid == 0) { if ($debug) { logmsg "Picking next best goal...\n"; } $val = $altval; } if ($debug) { logmsg "The nearest goal is number " . $activegoals[$val] . "," . $goalAoH[$activegoals[$val]]{Name} . ", which is at x=" . $goalAoH[$activegoals[$val]]{Appletx} . ", y=" . $goalAoH[$activegoals[$val]]{Applety} . ".\n"; } my $theta; my $finaltheta; # find delta x my $dx = abs($currentpos{x} - $goalAoH[$activegoals[$val]]{Appletx}); # find delta y my $dy = abs($currentpos{y} - $goalAoH[$activegoals[$val]]{Applety}); if ($currentpos{y} > ($goalAoH[$activegoals[$val]]{Applety})) { $theta = atan($dx/$dy); $dist = $dy/(cos($theta)); if ($currentpos{x} < ($goalAoH[$activegoals[$val]]{Appletx})) { $finaltheta = 270 + rad2deg($theta); } else { $finaltheta = 270 - rad2deg($theta); } } if ($currentpos{y} < ($goalAoH[$activegoals[$val]]{Applety})) { $theta = atan($dy/$dx); $dist = $dx/(cos($theta)); if ($currentpos{x} < ($goalAoH[$activegoals[$val]]{Appletx})) { $finaltheta = rad2deg($theta); } else { $finaltheta = 180 - rad2deg($theta); } } # Fix $finaltheta for inverted y-axis of applet $finaltheta = 360 - abs($finaltheta); # Bad news, we are already at the new goal if ($currentpos{y} == ($goalAoH[$activegoals[$val]]{Applety})) { logmsg "Uh oh, we are already at the new goal, something is wrong!\n"; } #find delta theta my $dtheta = abs($finaltheta - $currentpos{theta}); # Store total rot, trans time my $roundeddtheta = sprintf("%.0f", $dtheta); $simdata{rottime} = $roundeddtheta; my $roundeddist = sprintf("%.0f", $dist); $simdata{transtime} = $roundeddist; if ($debug) { logmsg "\$dx is $dx.\n"; logmsg "\$dy is $dy.\n"; logmsg "\$theta is " . rad2deg($theta) . ".\n"; logmsg "\$dtheta is " . $dtheta . ".\n"; logmsg "\$finaltheta is " . $finaltheta . ".\n"; logmsg "\$dist is $dist.\n"; logmsg "Total rotation time is $roundeddtheta seconds.\n"; logmsg "Total translation time is $roundeddist seconds.\n"; logmsg "Total time to target is " . ($roundeddtheta + $roundeddist) . " seconds.\n"; } # Store it $simdata{moving} = 1; $simdata{starttime} = time(); $simdata{targetpos}{x} = $goalAoH[$activegoals[$val]]{Appletx}; $simdata{targetpos}{y} = $goalAoH[$activegoals[$val]]{Applety}; $simdata{theta}{initial} = $currentpos{theta}; $simdata{theta}{final} = sprintf("%.0f", $finaltheta); $simshare->store(freeze(\%simdata)); flushsemaphores(); } } #} # End new goal setup } # Only reaches here if the robot is moving if (($#activegoals == 0) && ($activegoals[0] != 0)) { if ($debug) { logmsg "There aren't any goals, can't move..\n"; } } #else { # if ((($currentpos{x} - $simdata{targetpos}{x}) == 0) || (($currentpos{y} - $simdata{targetpos}{y}) == 0)) { # if (($currentpos{x} - $simdata{targetpos}{x}) == 0) { # print "Distance calculations are different when on the x axis!\n"; # } # if (($currentpos{y} - $simdata{targetpos}{y}) == 0) { # print "Distance calculations are different when on the y axis!\n"; # } # } #else { my $currenttime = time(); my $deltatime = $currenttime - $simdata{starttime}; # should be at target, set robot there and reset if (($simdata{starttime} + $simdata{rottime} + $simdata{transtime}) <= $currenttime) { if ($debug) { logmsg "Time to target expired, resetting for next goal...\n"; } $currentpos{x} = $simdata{targetpos}{x}; $currentpos{y} = $simdata{targetpos}{y}; #$currentpos{theta} = $theta; if (rad2deg($theta) <= 180) { $currentpos{theta} = $simdata{theta}{final}; } else { $currentpos{theta} = 360 - $simdata{theta}{final}; } # Clean up visited and expired goals for $i (0 .. $#activegoals) { if (($simdata{visited}{$activegoals[$i]} == 1) && ($currenttime > ($goalAoH[$activegoals[$i]]{Timestamp} + $visittimeout))) { $simdata{visited}{$activegoals[$i]} = 0; } } $simdata{moving} = 0; for $goal (0 .. $#goalAoH) { if ($goalAoH[$goal]{Appletx} == $currentpos{x}) { $simdata{visited}{$goal} = 1; $goalAoH[$goal]{Timestamp} = $currenttime; $goalshare->store(freeze(\@goalAoH)); if ($debug) { logmsg "We have arrived at " . $goalAoH[$goal]{Name} . ".\n"; } } } if (($simdata{targetpos}{x} eq '') || ($simdata{targetpos}{y} eq '')) { if ($debug) { "No target, skipping pushpos...\n"; } } else { # Send it all to shared memory $simshare->store(freeze(\%simdata)); pushpos($currentpos{x},$currentpos{y},$currentpos{theta}); } } if ($simdata{moving} == 1) { # find x my $x = abs($currentpos{x} - $simdata{targetpos}{x}); # find y my $y = abs($currentpos{y} - $simdata{targetpos}{y}); # find theta my $theta; my $dist; if ($currentpos{y} > $simdata{targetpos}{y}) { $theta = atan($x/$y); $dist = $y/(sin($theta)); } if ($currentpos{y} < $simdata{targetpos}{y}) { $theta = atan($y/$x); $dist = $x/(sin($theta)); } # Bad news, we are already at the new goal if ($currentpos{y} == $simdata{targetpos}{y}) { logmsg "Uh oh, we are already at the new goal. Something is wrong!\n"; } if ($debug) { logmsg "\$x is $x.\n"; logmsg "\$y is $y.\n"; logmsg "\$theta is " . rad2deg($theta) . ".\n"; logmsg "\$dist is $dist.\n"; } #} if (($simdata{starttime} + $simdata{rottime}) > $currenttime) { # Turn to target if ($currentpos{theta} != rad2deg($theta)) { my $oldpos = $currentpos{theta}; if (($simshare{theta}{final}) > rad2deg($theta)) { #if (($simshare{theta}{final}) > (360 - rad2deg($theta))) { logmsg "Testing bug condition 1!\n"; $currentpos{theta} = sprintf("%.0f", ($simdata{theta}{initial} + ($simdata{rottime} - $deltatime))); } else { $currentpos{theta} = sprintf("%.0f", ($simdata{theta}{final} - ($simdata{rottime} - $deltatime))); } my $timeleft = $simdata{rottime} - ($currenttime - $simdata{starttime}); if ($debug) { logmsg "Rotating " . abs($currentpos{theta} - $oldpos) . " degrees...\n"; logmsg "$timeleft seconds left of rotation cycle.\n"; } } else { if ($debug) { logmsg "oops, we don't want to over-rotate...\n"; } } } # Trans to target if ((($simdata{starttime} + $simdata{rottime}) < $currenttime) && (($simdata{starttime} + $simdata{rottime} + $simdata{transtime}) > $currenttime)) { #just to be sure #$currentpos{theta} = $simdata{theta}{final}; if (rad2deg($theta) <= 180) { $currentpos{theta} = $simdata{theta}{final}; } else { $currentpos{theta} = 360 - $simdata{theta}{final}; } my $dy; my $dx; # Find new distance to target $newdist = $simdata{transtime} - ($deltatime - $simdata{rottime}); # Find new y if ($simdata{targetpos}{y} > $currentpos{y}) { $dy = ($newdist * sin($theta)); $dx = ($newdist * cos($theta)); $currentpos{y} = sprintf("%.0f", ($simdata{targetpos}{y} - $dy)); } if ($simdata{targetpos}{y} < $currentpos{y}) { $dx = ($newdist * sin($theta)); $dy = ($newdist * cos($theta)); $currentpos{y} = sprintf("%.0f", ($simdata{targetpos}{y} + $dy)); } # Find new x if ($simdata{targetpos}{x} > $currentpos{x}) { $currentpos{x} = sprintf("%.0f", ($simdata{targetpos}{x} - $dx)); } if ($simdata{targetpos}{x} < $currentpos{x}) { $currentpos{x} = sprintf("%.0f", ($simdata{targetpos}{x} + $dx)); } my $transtimeleft = ($simdata{rottime} + $simdata{transtime}) - ($currenttime - $simdata{starttime}); if ($debug) { logmsg "Stored rottime is=" . $simdata{rottime} . ", stored transtime is=" . $simdata{transtime} . ".\n"; logmsg "$transtimeleft seconds left in translation cycle.\n"; logmsg "Total run time is " . ($currenttime - $simdata{starttime}) . " seconds.\n"; logmsg "Translating: distance=$newdist, x=" . $currentpos{x} . ", y=" . $currentpos{y} . ", theta=" . $currentpos{theta} . ".\n"; } } pushpos($currentpos{x},$currentpos{y},$currentpos{theta}); } # there are no goals, and we are in sim mode, just give the # position # Crude, but dynamic #pushpos(rand(357), rand(426), rand(360)); my %fretpos = %{thaw($posshare->fetch)}; %fretpos->{'x'}, $mapheight - %fretpos->{'y'}, %fretpos->{'theta'}; } else { my %fretpos = %{thaw($posshare->fetch)}; %fretpos->{'x'}, %fretpos->{'y'}, %fretpos->{'theta'}; } } # Load the goal file sub read_goal_file { loadglobals(); my $goalini = new Config::IniFiles( -file => $goalfile ); # load it all into an Array of Hashes my @GoalAoH; for (my $i = 0; $i <= 19; $i++) { if ($goalini->val( $i, 'Enabled' ) eq 'yes') { $GoalAoH[$i]{Name} = $goalini->val( $i, 'Name' ); $GoalAoH[$i]{URL} = $goalini->val( $i, 'URL' ); $GoalAoH[$i]{Appletx} = $goalini->val( $i, 'Appletx' ); $GoalAoH[$i]{Applety} = $goalini->val( $i, 'Applety' ); $GoalAoH[$i]{Realx} = $goalini->val( $i, 'Realx' ); $GoalAoH[$i]{Realy} = $goalini->val( $i, 'Realy' ); $GoalAoH[$i]{Enabled} = $goalini->val( $i, 'Enabled' ); # For goal visit timeout $GoalAoH[$i]{Timestamp} = 0; #print "\$i=" . $i . " is enabled\n"; } #else { # print "\$i=" . $i . " is disabled\n"; #} } my $frozengoals = freeze(\@GoalAoH); #print "\n\$frozengoals is " . length($frozengoals) . " bytes.\n"; #print "Storing in shared memory...\n"; $goalshare->store($frozengoals); my %MainHoH = %{thaw($globalshare->fetch)}; $MainHoH{mapheight} = $goalini->val( 'Map', 'Height' ); $globalshare->store(freeze(\%MainHoH)); #print "...and now retrieving\n"; #my @newgoalsAoH = @{thaw($goalshare->fetch)}; # #for $j (0 .. $#newgoalsAoH) { # print "$j is { "; # for $role (keys %{ $newgoalsAoH[$j] }) { # print "$role= $newgoalsAoH[$j]{$role}\n"; # } # print "}\n"; #} } # pop the goals AoH out of shared memory sub popgoals { @{thaw($goalshare->fetch)}; } # register vote for goal sub votegoal { ($goalnum, $clientip) = @_ ; ## Kludgy Client Fingerprinting Schema (TM) ;) # %votelist is a hash of arrays, with the key as the # client's IP address and the array element as the goal # number. my %votelist = %{thaw($voteshare->fetch)}; if ($votelist{$clientip}[$goalnum] == 1) { print "no\n"; print "I already have a vote from you for that goal.\n"; } else { $votelist{$clientip}[$goalnum] = 1; print "ok\n"; print "Vote accepted.\n"; $voteshare->store(freeze(\%votelist)); tallyvotes(); } touchclient($clientip); purgeclients(); } # Tally all votes sub tallyvotes { my %votelist = %{thaw($voteshare->fetch)}; my @mrgoals = popgoals(); my $votestotal = 0; my %votetally; my $a; my $b; my $c; my $d; my $i; my $j; my $nummatches = 0; my $numoldmatches = 0; my @activegoals = @{thaw($activegoalshare->fetch)}; my @newactivegoals; for $client (keys %votelist) { for $i (0 .. $#{ $votelist{$client} }) { if ($votelist{$client}[$i] == 1) { $votetally{$i}++; } } } # how many have we got? for $j (keys %votetally) { my $votestring; if ($votetally{$j} > 1) { $votestring = 'votes'; } else { $votestring = 'vote'; } print $votetally{$j} . " $votestring for " . $mrgoals[$j]{Name} . ".\n"; $votestotal += $votetally{$j}; } if (($votestotal != 0) && ((keys %votetally) != 0)) { my $votesaverage = sprintf("%.0f", $votestotal/(keys %votetally)); print "The total number of votes is " . $votestotal . ". The average number is " . $votesaverage . ".\n"; # Compare with current active votes # Testing stuff #print "\nContents of \%votetally:\n"; #for $b (keys %votetally) { # print $b . " is " . $votetally{$b} . ".\n"; #} #print "\nContents of \@activegoals:\n"; #for $d (0 .. $#activegoals) { # print $d . " is " . $activegoals[$d] . ".\n"; #} for $b (keys %votetally) { my $match; for $a (0 .. $#activegoals) { if ($votetally{$b} >= $votesaverage) { if ($activegoals[$a] eq $b) { $nummatches++; print "Match found:" . $activegoals[$a] . "=" . $b . ".\n"; } } } } for $c (keys %votetally) { if ($votetally{$c} >= $votesaverage) { $numoldmatches++; } else { print "Nope, " . $votetally{$c} . " isn't >= " . $votesaverage . ".\n"; } } if ($numoldmatches != $nummatches) { # We are only interested in new goals #if ($numoldmatches > $nummatches) { print "A new goal has been voted, alerting the planner!\n"; $semaphoreshare->store("1"); } my $index = 0; for $d (keys %votetally) { $newactivegoals[$index] = $d; $index++; } $activegoalshare->store(freeze(\@newactivegoals)); } else { print "There are no votes!\n"; } # Testing stuff print "\$numoldmatches=$numoldmatches.\n"; print "\$nummatches=$nummatches.\n"; print "\nContents of \@newactivegoals:\n"; for $d (0 .. $#newactivegoals) { print $d . " is " . $newactivegoals[$d] . ".\n"; } } # Print current vote tabulation (for debugging) sub printvotes { my %clienttds = %{thaw($clientshare->fetch)}; my @mrgoals = popgoals(); my %votelist = %{thaw($voteshare->fetch)}; my $timenow = time(); for $client (keys %votelist) { if ($client ne '') { print "->" . $client . " last voted " . ($timenow - $clienttds{$client}) . " seconds ago.\n"; for $i (0 .. $#{ $votelist{$client} }) { if ($votelist{$client}[$i] == 1) { print $mrgoals[$i]{Name} . "\n"; } } print "\n"; } } } # Return the selected voted goal sub getgoal { ($whichone) = @_ ; my @mrgoals = popgoals(); my @activegoals = @{thaw($activegoalshare->fetch)}; return($mrgoals[$activegoals[$whichone]]{Realx}, $mrgoals[$activegoals[$whichone]]{Realy}); } # How many goals are in @activegoals? sub howmanygoals { my @activegoals = @{thaw($activegoalshare->fetch)}; return $#activegoals; } # Initialize the client shared memory space sub flushclients { my %tds; $tds{'127.0.0.1'} = time(); $clientshare->store(freeze(\%tds)); } # Flush out semaphore shared memory sub flushsemaphores { $semaphoreshare->store("0"); } # Flush out vote shared memory space sub flushvotes { my %votelist; $votelist{'127.0.0.1'}[0] = 0; $voteshare->store(freeze(\%votelist)); } # Check if a new goal has been added sub newgoal { my $check = $semaphoreshare->fetch; if ($check eq '1') { return 1; } else { return 0; } } # # Components of the following sub have been borrowed from Charles # Kereke's IpMath.pl (http://homepages.waymark.net/~ckerekes/IpMath.pl) # #This sub converts an entire ip address or subnet mask to char sub IpToBin { my ($ip) = @_[0]; #this is the ip to convert my @octets, $bin; #Split up octets into the array @octets=split(/\./,$ip); #Append the decimal value of the octets $bin = unpack("c", $octets[0]); $bin .= unpack("c", $octets[1]); $bin .= unpack("c", $octets[2]); $bin .= unpack("c", $octets[3]); #return the result in $bin $bin; } 1;