From 11d4503c67235600af279b57b785e5657faaf63c Mon Sep 17 00:00:00 2001
From: Dirk Koopman <djk@tobit.co.uk>
Date: Wed, 12 Sep 2007 20:43:28 +0100
Subject: [PATCH] allow PC92(A/D) to be slugged and aggregated

Wait for a period of time before issuing pc92a or d records
and then aggregate them together as a collection of adds or deletes
in just (up to) two sentences, instead of one per event.

This has the side effect of (nearly) eliminating the "login, fire a
spot, logoff" merchants.
---
 perl/DXCommandmode.pm | 11 +++++++----
 perl/DXProt.pm        | 12 ++++++++++--
 perl/DXProtHandle.pm  | 43 ++++++++++++++++++++++++++++++++++++-------
 perl/Version.pm       |  2 +-
 4 files changed, 54 insertions(+), 14 deletions(-)

diff --git a/perl/DXCommandmode.pm b/perl/DXCommandmode.pm
index 47b68667..691e29b2 100644
--- a/perl/DXCommandmode.pm
+++ b/perl/DXCommandmode.pm
@@ -65,13 +65,14 @@ sub new
 	# routing, this must go out here to prevent race condx
 	my $pkg = shift;
 	my $call = shift;
-	my @rout = $main::routeroot->add_user($call, Route::here(1));
+#	my @rout = $main::routeroot->add_user($call, Route::here(1));
+	DXProt::_add_thingy($main::routeroot, [$call, 0, 0, 1]);
 
 	# ALWAYS output the user
 	my $ref = Route::User::get($call);
 	if ($ref) {
 		$main::me->route_pc16($main::mycall, undef, $main::routeroot, $ref);
-		$main::me->route_pc92a($main::mycall, undef, $main::routeroot, $ref);
+		$main::me->route_pc92a($main::mycall, undef, $main::routeroot, $ref) unless $DXProt::pc92_slug_changes;
 	}
 
 	return $self;
@@ -606,12 +607,14 @@ sub disconnect
 	my $uref = Route::User::get($call);
 	my @rout;
 	if ($uref) {
-		@rout = $main::routeroot->del_user($uref);
+#		@rout = $main::routeroot->del_user($uref);
+		@rout = DXProt::_del_thingy($main::routeroot, [$call, 0]);
+
 		dbg("B/C PC17 on $main::mycall for: $call") if isdbg('route');
 
 		# issue a pc17 to everybody interested
 		$main::me->route_pc17($main::mycall, undef, $main::routeroot, $uref);
-		$main::me->route_pc92d($main::mycall, undef, $main::routeroot, $uref);
+		$main::me->route_pc92d($main::mycall, undef, $main::routeroot, $uref) unless $DXProt::pc92_slug_changes;
 	} else {
 		confess "trying to disconnect a non existant user $call";
 	}
diff --git a/perl/DXProt.pm b/perl/DXProt.pm
index 42479d86..e6f04a7f 100644
--- a/perl/DXProt.pm
+++ b/perl/DXProt.pm
@@ -45,7 +45,7 @@ use vars qw($pc11_max_age $pc23_max_age $last_pc50 $eph_restime $eph_info_restim
 			$allowzero $decode_dk0wcy $send_opernam @checklist
 			$eph_pc15_restime $pc92_update_period $pc92_obs_timeout
 			%pc92_find $pc92_find_timeout $pc92_short_update_period
-			$next_pc92_obs_timeout
+			$next_pc92_obs_timeout $pc92_slug_changes $last_pc92_slug
 		   );
 
 $pc11_max_age = 1*3600;			# the maximum age for an incoming 'real-time' pc11
@@ -82,7 +82,6 @@ $pc92_obs_timeout = 60*60; # the time between obscount for incoming countdowns
 $next_pc92_obs_timeout = $main::systime + 60*60; # the time between obscount countdowns
 
 
-
 @checklist =
 (
  [ qw(i c c m bp bc c) ],			# pc10
@@ -481,9 +480,17 @@ sub process
 				dbg("ROUTE: pc92 broadcast candidate: $dxchan->{call}") if isdbg('obscount');
 				if ($dxchan == $main::me || !$dxchan->{do_pc9x}) {
 					$dxchan->broadcast_pc92_update($dxchan->{call});
+					$last_pc92_slug = 0 if $dxchan == $main::me;
 				}
 			}
 		}
+
+		if ($pc92_slug_changes && $main::systime >= $last_pc92_slug + $pc92_slug_changes) {
+			my ($add, $del) = gen_pc92_changes();
+			$main::me->route_pc92d($main::mycall, undef, $main::routeroot, @$del) if @$del;
+			$main::me->route_pc92a($main::mycall, undef, $main::routeroot, @$add) if @$add;
+			clear_pc92_changes();
+		}
 	}
 
 	if ($main::systime - 3600 > $last_hour) {
@@ -866,6 +873,7 @@ sub gen_my_pc92_config
 	my $node = shift;
 
 	if ($node->{call} eq $main::mycall) {
+		clear_pc92_changes();		# remove any slugged data, we are generating it as now
 		my @dxchan = grep { $_->call ne $main::mycall && !$_->{isolate} } DXChannel::get_all();
 		dbg("ROUTE: all dxchan: " . join(',', map{$_->{call}} @dxchan)) if isdbg('routelow');
 		my @localnodes = map { my $r = Route::get($_->{call}); $r ? $r : () } @dxchan;
diff --git a/perl/DXProtHandle.pm b/perl/DXProtHandle.pm
index 9e0f5ec4..1b350615 100644
--- a/perl/DXProtHandle.pm
+++ b/perl/DXProtHandle.pm
@@ -44,14 +44,15 @@ use vars qw($pc11_max_age $pc23_max_age $last_pc50 $eph_restime $eph_info_restim
 			%nodehops $baddx $badspotter $badnode $censorpc
 			$allowzero $decode_dk0wcy $send_opernam @checklist
 			$eph_pc15_restime $pc9x_past_age $pc9x_future_age
-			$pc10_dupe_age
+			$pc10_dupe_age $pc92_slug_changes $last_pc92_slug
 		   );
 
 $pc9x_past_age = 62*60;			# maximum age in the past of a px9x (a config record might be the only
 								# thing a node might send - once an hour)
 $pc9x_future_age = 5*60;		# maximum age in the future ditto
 $pc10_dupe_age = 45;			# just something to catch duplicate PC10->PC93 conversions
-
+$pc92_slug_changes = 0;			# slug any changes going outward for this long
+$last_pc92_slug = 0;			# the last time we sent out any delayed add or del PC92s
 
 # incoming talk commands
 sub handle_10
@@ -766,7 +767,7 @@ sub handle_19
 #		$self->route_pc21($self->{call}, $line, @rout);
 		$self->route_pc19($self->{call}, $line, @rout);
 	}
-	if (@pc92out) {
+	if (@pc92out && !$pc92_slug_changes) {
 		$self->route_pc92a($main::mycall, $line, $main::routeroot, @pc92out) if $self->{state} eq 'normal';
 	}
 }
@@ -1364,6 +1365,9 @@ sub _encode_pc92_call
 	return "$flag$call$extra";
 }
 
+my %things_add;
+my %things_del;
+
 sub _add_thingy
 {
 	my $parent = shift;
@@ -1379,6 +1383,10 @@ sub _add_thingy
 			dbg("ROUTE: added user $call to " . $parent->call) if isdbg('routelow');
 			@rout = $parent->add_user($call, Route::here($here));
 		}
+		if ($pc92_slug_changes && $parent == $main::routeroot) {
+			$things_add{$call} = Route::get($call);
+			delete $things_del{$call};
+		}
 	}
 	return @rout;
 }
@@ -1390,19 +1398,40 @@ sub _del_thingy
 	my ($call, $is_node, $is_extnode, $here, $version, $build) = @$s;
 	my @rout;
 	if ($call) {
+		my $ref;
 		if ($is_node) {
-			my $nref = Route::Node::get($call);
+			$ref = Route::Node::get($call);
 			dbg("ROUTE: deleting node $call from " . $parent->call) if isdbg('routelow');
-			@rout = $nref->del($parent) if $nref;
+			@rout = $ref->del($parent) if $ref;
 		} else {
-			my $uref = Route::User::get($call);
+			$ref = Route::User::get($call);
 			dbg("ROUTE: deleting user $call from " . $parent->call) if isdbg('routelow');
-			@rout = $parent->del_user($uref) if $uref;
+			@rout = $parent->del_user($ref) if $ref;
+		}
+		if ($pc92_slug_changes && $parent == $main::routeroot) {
+			$things_del{$call} = $ref unless exists $things_add{$call};
+			delete $things_add{$call};
 		}
 	}
 	return @rout;
 }
 
+# this will only happen if we are slugging changes and
+# there are some changes to be sent, it will create an add or a delete
+# or both
+sub gen_pc92_changes
+{
+	my @add = values %things_add;
+	my @del = values %things_del;
+	return (\@add, \@del);
+}
+
+sub clear_pc92_changes
+{
+	%things_add = %things_del = ();
+	$last_pc92_slug = $main::systime;
+}
+
 my $_last_time;
 my $_last_occurs;
 
diff --git a/perl/Version.pm b/perl/Version.pm
index 67b77d78..d72986dc 100644
--- a/perl/Version.pm
+++ b/perl/Version.pm
@@ -11,6 +11,6 @@ use vars qw($version $subversion $build);
 
 $version = '1.54';
 $subversion = '0';
-$build = '137';
+$build = '138';
 
 1;
-- 
2.43.0