+# 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;
+my $_last_pc9x_id;
+
+sub last_pc9x_id
+{
+ return $_last_pc9x_id;
+}
+
+sub gen_pc9x_t
+{
+ if (!$_last_time || $_last_time != $main::systime) {
+ $_last_time = $main::systime;
+ $_last_occurs = 0;
+ return $_last_pc9x_id = $_last_time - $main::systime_daystart;
+ } else {
+ $_last_occurs++;
+ return $_last_pc9x_id = sprintf "%d.%02d", $_last_time - $main::systime_daystart, $_last_occurs;
+ }
+}
+
+sub check_pc9x_t
+{
+ my $call = shift;
+ my $t = shift;
+ my $pc = shift;
+ my $create = shift;
+
+ # check that the time is between 0 >= $t < 86400
+ unless ($t >= 0 && $t < 86400) {
+ dbg("PCPROT: time invalid t: $t, ignored") if isdbg('chanerr');
+ return undef;
+ }
+
+ # check that the time of this pc9x is within tolerance (default 15 mins either way)
+ my $now = $main::systime - $main::systime_daystart ;
+ my $diff = abs($now - $t);
+ unless ($diff < $pc9x_time_tolerance || 86400 - $diff < $pc9x_time_tolerance) {
+ my $c = ref $call ? $call->call : $call;
+ dbg("PC9XERR: $c time out of range t: $t now: $now diff: $diff > $pc9x_time_tolerance, ignored") if isdbg('chan');
+ return undef;
+ }
+
+ my $parent = ref $call ? $call : Route::Node::get($call);
+ if ($parent) {
+ # we only do this for external calls whose routing table
+ # record come and go. The reference for mycall is permanent
+ # and not that frequently used, it also never times out, so
+ # the id on it is completely unreliable. Besides, only commands
+ # originating on this box will go through this code...
+ if ($parent->call ne $main::mycall) {
+ my $lastid = $parent->lastid;
+ if (defined $lastid) {
+ if ($t < $lastid) {
+ # note that this is where we determine whether this pc9x has come in yesterday
+ # but is still greater (modulo 86400) than the lastid or is simply an old
+ # duplicate sentence. To determine this we need to do some module 86400
+ # arithmetic. High numbers mean that this is an old duplicate sentence,
+ # low numbers that it is a new sentence.
+ #
+ # Typically you will see yesterday being taken on $t = 84, $lastid = 86235
+ # and old dupes with $t = 234, $lastid = 256 (which give answers 249 and
+ # 86378 respectively in the calculation below).
+ #
+ if ($t+86400-$lastid > $pc9x_past_age) {
+ dbg("PCPROT: dup id on $t <= lastid $lastid, ignored") if isdbg('chanerr') || isdbg('pc92dedupe');
+ return undef;
+ }
+ } elsif ($t == $lastid) {
+ dbg("PCPROT: dup id on $t == lastid $lastid, ignored") if isdbg('chanerr') || isdbg('pc92dedupe');
+ return undef;
+ } else {
+ # check that if we have a low number in lastid that yesterday's numbers
+ # (likely in the 85000+ area) don't override them, thus causing flip flopping
+ if ($lastid+86400-$t < $pc9x_past_age) {
+ dbg("PCPROT: dup id on $t in yesterday, lastid $lastid, ignored") if isdbg('chanerr') || isdbg('pc92dedupe');
+ return undef;
+ }
+ }
+ }
+ }
+ } elsif ($create) {
+ $parent = Route::Node->new($call);
+ } else {
+ dbg("PCPROT: $call does not exist, ignored") if isdbg('pc92dedupe');
+ return undef;
+ }
+ if (isdbg('pc92dedupe')) {
+ my $exists = exists $parent->{lastid}; # naughty, naughty :-)
+ my $val = $parent->{lastid};
+ my $s = $exists ? (defined $val ? $val : 'exists/undef') : 'undef';
+ dbg("PCPROT: $call pc92 id lastid $s -> $t");
+ }
+ $parent->lastid($t);
+
+ return $parent;
+}
+
+sub pc92_handle_first_slot
+{
+ my $self = shift;
+ my $slot = shift;
+ my $parent = shift;
+ my $t = shift;
+ my $hops = shift;
+ my $oparent = $parent;
+
+ my @radd;
+
+ my ($call, $is_node, $is_extnode, $here, $version, $build) = @$slot;
+ if ($call && $is_node) {
+ if ($call eq $main::mycall) {
+ dbg("PCPROT: $call looped back onto \$main::mycall ($main::mycall), ignored") if isdbg('chan');
+ return;
+ }
+ if ($call eq $main::myalias) {
+ dbg("PCPROT: $call looped back onto \$main::myalias ($main::myalias), ignored") if isdbg('chan');
+ return;
+ }
+ # this is only accepted from my "self".
+ # this also kills configs from PC92 nodes with external PC19 nodes that are also
+ # locally connected. Local nodes always take precedence. But we remember the lastid
+ # to try to reduce the number of dupe PC92s for this external node.
+ if (DXChannel::get($call) && $call ne $self->{call}) {
+ $parent = check_pc9x_t($call, $t, 92); # this will update the lastid time
+ dbg("PCPROT: locally connected node $call from other another node $self->{call}, ignored") if isdbg('chanerr');
+ return;
+ }
+ if ($is_extnode) {
+ # reparent to external node (note that we must have received a 'C' or 'A' record
+ # from the true parent node for this external before we get one for the this node
+ unless ($parent = Route::Node::get($call)) {
+ if ($is_extnode && $oparent) {
+ @radd = _add_thingy($oparent, $slot, $self, $hops);
+ $parent = $radd[0];
+ } else {
+ dbg("PCPROT: no previous C or A for this external node received, ignored") if isdbg('chanerr');
+ return;
+ }
+ }
+ $parent = check_pc9x_t($call, $t, 92) || return;
+ $parent->via_pc92(1);
+ $parent->PC92C_dxchan($self->{call}, $hops);
+ }
+ } else {
+ dbg("PCPROT: must be \$mycall or external node as first entry, ignored") if isdbg('chanerr');
+ return;
+ }
+ $parent->here(Route::here($here));
+ $parent->version($version || $pc19_version) if $version;
+ $parent->build($build) if $build;
+ $parent->PC92C_dxchan($self->{call}, $hops) unless $self->{call} eq $parent->call;
+ return ($parent, @radd);
+}
+