1. first cut with new routing code. Created NEW_ROUTE branch
[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
236         my $dxchan = DXChannel->get($self->{call});
237         if ($dxchan) {
238                 return (grep $dxchan == $_, @_) ? () : ($dxchan);
239         }
240         
241         # it isn't, build up a list of dxchannels and possible ping times 
242         # for all the candidates.
243         my @dxchan = @_;
244         foreach my $p (@{$self->{parent}}) {
245                 my $ref = $self->get($p);
246                 push @dxchan, $ref->alldxchan(@dxchan);
247         }
248         return @dxchan;
249 }
250
251 sub dxchan
252 {
253         my $self = shift;
254         my $dxchan;
255         my @dxchan = $self->alldxchan;
256         return undef unless @dxchan;
257         
258         # determine the minimum ping channel
259         my $minping = 99999999;
260         foreach my $dxc (@dxchan) {
261                 my $p = $dxc->pingave;
262                 if (defined $p  && $p < $minping) {
263                         $minping = $p;
264                         $dxchan = $dxc;
265                 }
266         }
267         $dxchan = shift @dxchan unless $dxchan;
268         return $dxchan;
269 }
270
271 #
272 # track destruction
273 #
274
275 sub DESTROY
276 {
277         my $self = shift;
278         my $pkg = ref $self;
279         
280         dbg('routelow', "$pkg $self->{call} destroyed");
281 }
282
283 no strict;
284 #
285 # return a list of valid elements 
286
287
288 sub fields
289 {
290         my $pkg = shift;
291         $pkg = ref $pkg if ref $pkg;
292     my $val = "${pkg}::valid";
293         my @out = keys %$val;
294         push @out, keys %valid;
295         return @out;
296 }
297
298 #
299 # return a prompt for a field
300 #
301
302 sub field_prompt
303
304         my ($self, $ele) = @_;
305         my $pkg = ref $self;
306     my $val = "${pkg}::valid";
307         return $val->{$ele} || $valid{$ele};
308 }
309
310 #
311 # generic AUTOLOAD for accessors
312 #
313 sub AUTOLOAD
314 {
315         my $self = shift;
316         my $name = $AUTOLOAD;
317         return if $name =~ /::DESTROY$/;
318         $name =~ s/.*:://o;
319   
320         confess "Non-existant field '$AUTOLOAD'" if !$valid{$name};
321
322         # this clever line of code creates a subroutine which takes over from autoload
323         # from OO Perl - Conway
324 #       *{$AUTOLOAD} = sub {@_ > 1 ? $_[0]->{$name} = $_[1] : $_[0]->{$name}} ;
325     @_ ? $self->{$name} = shift : $self->{$name} ;
326 }
327
328 1;