some fixed
[spider.git] / perl / Route.pm
1 #!/usr/bin/perl
2 #
3 # This module impliments the abstracted routing for all protocols and
4 # is probably what I SHOULD have done the first time. 
5 #
6 # Heyho.
7 #
8 # This is just a container class which I expect to subclass 
9 #
10 # Copyright (c) 2001 Dirk Koopman G1TLH
11 #
12 # $Id$
13
14
15 package Route;
16
17 use DXDebug;
18 use DXChannel;
19 use Prefix;
20
21 use strict;
22
23 use vars qw(%list %valid $filterdef);
24
25 %valid = (
26                   call => "0,Callsign",
27                   flags => "0,Flags,phex",
28                   dxcc => '0,Country Code',
29                   itu => '0,ITU Zone',
30                   cq => '0,CQ Zone',
31                  );
32
33 $filterdef = bless ([
34                           # tag, sort, field, priv, special parser 
35                           ['channel', 'c', 0],
36                           ['channel_dxcc', 'n', 1],
37                           ['channel_itu', 'n', 2],
38                           ['channel_zone', 'n', 3],
39                           ['call', 'c', 4],
40                           ['call_dxcc', 'n', 5],
41                           ['call_itu', 'n', 6],
42                           ['call_zone', 'n', 7],
43                          ], 'Filter::Cmd');
44
45
46 sub new
47 {
48         my ($pkg, $call) = @_;
49         $pkg = ref $pkg if ref $pkg;
50
51         my $self = bless {call => $call}, $pkg;
52         dbg('routelow', "create $pkg with $call");
53
54         # add in all the dxcc, itu, zone info
55         my @dxcc = Prefix::extract($call);
56         if (@dxcc > 0) {
57                 $self->{dxcc} = $dxcc[1]->dxcc;
58                 $self->{itu} = $dxcc[1]->itu;
59                 $self->{cq} = $dxcc[1]->cq;                                             
60         }
61         
62         return $self; 
63 }
64
65 #
66 # get a callsign from a passed reference or a string
67 #
68
69 sub _getcall
70 {
71         my $self = shift;
72         my $thingy = shift;
73         $thingy = $self unless $thingy;
74         $thingy = $thingy->call if ref $thingy;
75         $thingy = uc $thingy if $thingy;
76         return $thingy;
77 }
78
79
80 # add and delete a callsign to/from a list
81 #
82
83 sub _addlist
84 {
85         my $self = shift;
86         my $field = shift;
87         foreach my $c (@_) {
88                 my $call = _getcall($c);
89                 unless (grep {$_ eq $call} @{$self->{$field}}) {
90                         push @{$self->{$field}}, $call;
91                         dbg('routelow', ref($self) . " adding $call to " . $self->{call} . "->\{$field\}");
92                 }
93         }
94         return $self->{$field};
95 }
96
97 sub _dellist
98 {
99         my $self = shift;
100         my $field = shift;
101         foreach my $c (@_) {
102                 my $call = _getcall($c);
103                 if (grep {$_ eq $call} @{$self->{$field}}) {
104                         $self->{$field} = [ grep {$_ ne $call} @{$self->{$field}} ];
105                         dbg('routelow', ref($self) . " deleting $call from " . $self->{call} . "->\{$field\}");
106                 }
107         }
108         return $self->{$field};
109 }
110
111 #
112 # flag field constructors/enquirers
113 #
114
115 sub here
116 {
117         my $self = shift;
118         my $r = shift;
119         return $self ? 2 : 0 unless ref $self;
120         return ($self->{flags} & 2) ? 1 : 0 unless $r;
121         $self->{flags} = (($self->{flags} & ~2) | ($r ? 1 : 0));
122         return $r ? 1 : 0;
123 }
124
125 sub conf
126 {
127         my $self = shift;
128         my $r = shift;
129         return $self ? 1 : 0 unless ref $self;
130         return ($self->{flags} & 1) ? 1 : 0 unless $r;
131         $self->{flags} = (($self->{flags} & ~1) | ($r ? 1 : 0));
132         return $r ? 1 : 0;
133 }
134
135 sub parents
136 {
137         my $self = shift;
138         return @{$self->{parent}};
139 }
140
141
142 # display routines
143 #
144
145 sub user_call
146 {
147         my $self = shift;
148         my $call = sprintf "%s", $self->{call};
149         return $self->here ? "$call" : "($call)";
150 }
151
152 sub config
153 {
154         my $self = shift;
155         my $nodes_only = shift;
156         my $level = shift;
157         my @out;
158         my $line;
159         my $call = $self->user_call;
160         my $printit = 1;
161
162         # allow ranges
163         if (@_) {
164                 $printit = grep $call =~ m|$_|, @_;
165         }
166
167         if ($printit) {
168                 $line = ' ' x ($level*2) . "$call";
169                 $call = ' ' x length $call; 
170                 unless ($nodes_only) {
171                         if (@{$self->{users}}) {
172                                 $line .= '->';
173                                 foreach my $ucall (sort @{$self->{users}}) {
174                                         my $uref = Route::User::get($ucall);
175                                         my $c;
176                                         if ($uref) {
177                                                 $c = $uref->user_call;
178                                         } else {
179                                                 $c = "$ucall?";
180                                         }
181                                         if ((length $line) + (length $c) + 1 < 79) {
182                                                 $line .= $c . ' ';
183                                         } else {
184                                                 $line =~ s/\s+$//;
185                                                 push @out, $line;
186                                                 $line = ' ' x ($level*2) . "$call->$c ";
187                                         }
188                                 }
189                         }
190                 }
191                 $line =~ s/->$//g;
192                 $line =~ s/\s+$//;
193                 push @out, $line if length $line;
194         }
195         
196         foreach my $ncall (sort @{$self->{nodes}}) {
197                 my $nref = Route::Node::get($ncall);
198
199                 if ($nref) {
200                         my $c = $nref->user_call;
201                         push @out, $nref->config($nodes_only, $level+1, @_);
202                 } else {
203                         push @out, ' ' x (($level+1)*2)  . "$ncall?" if @_ == 0 || (@_ && grep $ncall =~ m|$_|, @_); 
204                 }
205         }
206
207         return @out;
208 }
209
210 sub cluster
211 {
212         my $nodes = Route::Node::count();
213         my $tot = Route::User::count();
214         my $users = scalar DXCommandmode::get_all();
215         my $maxusers = Route::User::max();
216         my $uptime = main::uptime();
217         
218         return " $nodes nodes, $users local / $tot total users  Max users $maxusers  Uptime $uptime";
219 }
220
221 #
222 # routing things
223 #
224
225 sub get
226 {
227         my $call = shift;
228         return Route::Node::get($call) || Route::User::get($call);
229 }
230
231 # find all the possible dxchannels which this object might be on
232 sub alldxchan
233 {
234         my $self = shift;
235         my @dxchan;
236         my $dxchan = DXChannel->get($self->{call});
237         push @dxchan, $dxchan if $dxchan;
238         
239         # it isn't, build up a list of dxchannels and possible ping times 
240         # for all the candidates.
241         foreach my $p (@{$self->{parent}}) {
242                 my $dxchan = DXChannel->get($p);
243                 if ($dxchan) {
244                         push @dxchan, $dxchan if grep $dxchan ne $_, @dxchan;
245                 } else {
246                         next if $p eq $main::mycall; # the root
247                         my $ref = $self->get($p);
248                         push @dxchan, $ref->alldxchan if $ref;
249                 }
250         }
251         return @dxchan;
252 }
253
254 sub dxchan
255 {
256         my $self = shift;
257         my $dxchan = DXChannel->get($self->{call});
258         return $dxchan = $dxchan;
259         
260         my @dxchan = $self->alldxchan;
261         return undef unless @dxchan;
262         
263         # determine the minimum ping channel
264         my $minping = 99999999;
265         foreach my $dxc (@dxchan) {
266                 my $p = $dxc->pingave;
267                 if (defined $p  && $p < $minping) {
268                         $minping = $p;
269                         $dxchan = $dxc;
270                 }
271         }
272         $dxchan = shift @dxchan unless $dxchan;
273         return $dxchan;
274 }
275
276 #
277 # track destruction
278 #
279
280 sub DESTROY
281 {
282         my $self = shift;
283         my $pkg = ref $self;
284         
285         dbg('routelow', "$pkg $self->{call} destroyed");
286 }
287
288 no strict;
289 #
290 # return a list of valid elements 
291
292
293 sub fields
294 {
295         my $pkg = shift;
296         $pkg = ref $pkg if ref $pkg;
297     my $val = "${pkg}::valid";
298         my @out = keys %$val;
299         push @out, keys %valid;
300         return @out;
301 }
302
303 #
304 # return a prompt for a field
305 #
306
307 sub field_prompt
308
309         my ($self, $ele) = @_;
310         my $pkg = ref $self;
311     my $val = "${pkg}::valid";
312         return $val->{$ele} || $valid{$ele};
313 }
314
315 #
316 # generic AUTOLOAD for accessors
317 #
318 sub AUTOLOAD
319 {
320         my $self = shift;
321         my $name = $AUTOLOAD;
322         return if $name =~ /::DESTROY$/;
323         $name =~ s/.*:://o;
324   
325         confess "Non-existant field '$AUTOLOAD'" if !$valid{$name};
326
327         # this clever line of code creates a subroutine which takes over from autoload
328         # from OO Perl - Conway
329 #       *{$AUTOLOAD} = sub {@_ > 1 ? $_[0]->{$name} = $_[1] : $_[0]->{$name}} ;
330     @_ ? $self->{$name} = shift : $self->{$name} ;
331 }
332
333 1;