+# 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;
+
+ 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 || 0;
+ if ($t < $lastid) {
+ if ($t+86400-$lastid > $pc9x_past_age) {
+ dbg("PCPROT: dup id on $t <= $lastid, ignored") if isdbg('chanerr');
+ return;
+ }
+ } elsif ($t == $lastid) {
+ dbg("PCPROT: dup id on $t == $lastid, ignored") if isdbg('chanerr');
+ return;
+ } else {
+ # $t > $lastid, check that the timestamp offered isn't too far away from 'now'
+ if ($t+$main::systime_daystart-$main::systime > $pc9x_future_age ) {
+ dbg("PCPROT: id $t too far in the future, ignored") if isdbg('chanerr');
+ return;
+ }
+ }
+ }
+ } elsif ($create) {
+ $parent = Route::Node->new($call);
+ }
+ $parent->lastid($t) if $parent;
+
+ return $parent;
+}
+
+sub pc92_handle_first_slot
+{
+ my $self = shift;
+ my $slot = shift;
+ my $parent = shift;
+ my $t = 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, ignored") if isdbg('chanerr');
+ 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);
+ $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});
+ }
+ } 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 && $build > $parent->build;
+ $parent->PC92C_dxchan($self->{call}) unless $self->{call} eq $parent->call;
+ return ($parent, @radd);
+}
+