added Windows only BPQ interface from John G8BPQ
[spider.git] / perl / BPQMsg.pm
diff --git a/perl/BPQMsg.pm b/perl/BPQMsg.pm
new file mode 100644 (file)
index 0000000..e5ca707
--- /dev/null
@@ -0,0 +1,347 @@
+#
+# This class is the internal subclass that deals with the G8BPQ switch connections
+#
+# Written by John Wiseman G8BPQ Jan 2006
+#
+# Based on AGWMsg.pm Copyright (c) 2001 - Dirk Koopman G1TLH
+#
+
+package BPQMsg;
+
+use strict;
+use Msg;
+use BPQConnect;
+use DXDebug;
+
+use vars qw(@ISA @outqueue $send_offset $inmsg $rproc $noports
+                       %circuit $total_in $total_out);
+
+@ISA = qw(Msg ExtMsg);
+@outqueue = ();
+$send_offset = 0;
+$inmsg = '';
+$rproc = undef;
+$noports = 0;
+%circuit = ();
+$total_in = $total_out = 0;
+
+my $GetFreeBuffs;
+my $FindFreeStream;
+my $SetAppl;
+my $SessionState;
+my $GetCallsign;
+my $SendMsg;
+my $GetMsg;
+my $RXCount;
+my $DeallocateStream;
+my $SessionControl;
+
+my @Stream;
+
+my $Buffers;
+
+sub init
+{
+       return unless $enable;
+
+       eval {
+               require Win32::API;
+       };
+       if ($@) {
+               $enable = 0;
+               dbg("BPQWin disabled because Win32::API cannot be loaded");
+               return;
+       } else {
+               Win32::API->import;
+       }
+
+       $rproc = shift;
+
+       dbg("BPQ initialising...");
+
+       $GetFreeBuffs = Win32::API->new("bpq32", "int _GetFreeBuffs\@0()");
+    $FindFreeStream = Win32::API->new("bpq32", "int _FindFreeStream\@0()");
+    $SetAppl = Win32::API->new("bpq32", "int _SetAppl\@12(int a, int b, int c)");
+    $SessionState = Win32::API->new("bpq32", "DWORD _SessionState\@12(DWORD stream, LPDWORD state, LPDWORD change)");
+       $GetCallsign = new Win32::API("bpq32", "_GetCallsign\@8",'NP','N');
+       $SendMsg = new Win32::API("bpq32","_SendMsg\@12",'NPN','N');
+       $RXCount = new Win32::API("bpq32","_RXCount\@4",'N','N');
+       $GetMsg = Win32::API->new("bpq32","_GetMsgPerl\@8",'NP','N');
+
+       $DeallocateStream = Win32::API->new("bpq32","_DeallocateStream\@4",'N','N');
+    $SessionControl = Win32::API->new("bpq32", "int _SessionControl\@12(int a, int b, int c)");
+
+       if (!defined $GetMsg) {
+               $GetMsg = Win32::API->new("bpqperl","_GetMsgPerl\@8",'NP','N');
+       }
+
+       if (!defined $GetMsg) {
+               dbg ("Can't find routine 'GetMsgPerl' - is bpqperl.dll available?");
+       }
+
+       $Buffers = 0;
+
+       if (defined $GetFreeBuffs && defined $GetMsg) {
+               my $s;
+
+               $Buffers = $GetFreeBuffs->Call();
+
+               dbg("G8BPQ Free Buffers = $Buffers") if isdbg('bpq');
+
+               $s = "BPQ Streams:";
+
+               for (my $i = 1; $i <= $BPQStreams; $i++) {
+
+                       $Stream[$i] = $FindFreeStream->Call();
+
+                       $s .= " $Stream[$i]";
+
+                       $SetAppl->Call($Stream[$i], 0, $ApplMask);
+
+               }
+
+               dbg($s) if isdbg('bpq');
+       } else {
+
+               dbg("Couldn't initialise BPQ32 switch, BPQ disabled");
+               $enable = 0;
+       }
+}
+
+sub finish
+{
+       return unless $enable;
+
+       dbg("BPQ Closing..") if isdbg('bpq');
+
+       return unless $Buffers;
+
+       for (my $i = 1; $i <= $BPQStreams; $i++) {
+               $SetAppl->Call($Stream[$i], 0, 0);
+               $SessionControl->Call($Stream[$i], 2, 0); # Disconnect
+               $DeallocateStream->Call($Stream[$i]);
+       }
+}
+
+sub login
+{
+       goto &main::login;                      # save some writing, this was the default
+}
+
+sub active
+{
+       dbg("BPQ is active called") if isdbg('bpq');
+       return $Buffers;
+}
+
+
+sub connect
+{
+
+       return unless $Buffers;
+
+       my ($conn, $line) = @_;
+       my ($port, $call) = split /\s+/, $line;
+
+
+       dbg("BPQ Outgoing Connect  $conn $port $call") if isdbg('bpq');
+
+
+       for (my $i = $BPQStreams; $i > 0; $i--) {
+               my $inuse = $circuit{$Stream[$i]};
+
+               if (not $inuse) {               # Active connection?
+
+                       dbg("BPQ Outgoing Connect using stream $i") if isdbg('bpq');
+
+                       $conn->{bpqstream} = $Stream[$i];
+                       $conn->{lineend} = "\cM";
+                       $conn->{incoming} = 0;
+                       $conn->{csort} = 'ax25';
+                       $conn->{bpqcall} = uc $call;
+                       $circuit{$Stream[$i]} = $conn;
+
+                       $SessionControl->Call($Stream[$i], 1, 0); # Connect
+
+                       $conn->{state} = 'WC';
+
+                       return 1;
+
+               }
+
+       }
+
+       # No free streams
+       dbg("BPQ Outgoing Connect - No streams available") if isdbg('bpq');
+
+       $conn->{bpqstream} = 0;         # So we can tidy up
+       $circuit{0} = $conn;
+       return 0;
+}
+
+sub in_disconnect
+{
+       my $conn = shift;
+       dbg( "in_disconnect $conn $circuit{$conn->{bpqstream}}") if isdbg('bpq');
+       delete $circuit{$conn->{bpqstream}};
+       $conn->SUPER::disconnect;
+}
+
+sub disconnect
+{
+
+       return unless $enable && $Buffers;
+
+       my $conn = shift;
+
+       delete $circuit{$conn->{bpqstream}};
+
+       $conn->SUPER::disconnect;
+
+       if ($conn->{bpqstream}) {       # not if stream = 0!
+               $SessionControl->Call($conn->{bpqstream}, 2, 0); # Disconnect
+       }
+}
+
+sub enqueue
+{
+
+       return unless $Buffers;
+
+       my ($conn, $msg) = @_;
+
+       if ($msg =~ /^D/) {
+               $msg =~ s/^[-\w]+\|//;
+               #               _sendf('Y', $main::mycall, $conn->{call}, $conn->{bpqstream}, $conn->{agwpid});
+               #               _sendf('D', $main::mycall, $conn->{bpqcall}, $conn->{bpqstream}, $conn->{agwpid}, $msg . $conn->{lineend});
+
+               $msg = $msg . $conn->{lineend};
+
+               my $len = length($msg);
+               $SendMsg->Call($conn->{bpqstream}, $msg, $len);
+               dbg("BPQ Data Out port: $conn->{bpqstream}   length: $len \"$msg\"") if isdbg('bpq');
+       }
+}
+
+sub process
+{
+       return unless $enable && $Buffers;
+
+       my $state=0;
+       my $change=0;
+
+       for (my $i = 1; $i <= $BPQStreams; $i++) {
+               $SessionState->Call($Stream[$i], $state, $change);
+
+               if ($change) {
+                       dbg("Stream $Stream[$i] newstate $state") if isdbg('bpq');
+
+                       if ($state == 0) {
+                               # Disconnected
+
+                               my $conn = $circuit{$Stream[$i]};
+
+                               if ($conn) {            # Active connection?
+                                       &{$conn->{eproc}}() if $conn->{eproc};
+                                       $conn->in_disconnect;
+                               }
+
+                       }
+
+                       if ($state) {
+
+                               # Incoming call
+
+                               my $call="            ";
+
+                               $GetCallsign->Call($Stream[$i],$call);
+
+                               for ($call) {   # trim whitespace in $variable, cheap
+                               s/^\s+//;
+                                       s/\s+$//;
+                               }
+
+                               dbg("BPQ Connect Stream $Stream[$i] $call") if isdbg('bpq');
+
+                               my $conn =  $circuit{$Stream[$i]};;
+
+                               if ($conn) {
+
+                                       # Connection already exists - if we are connecting out this is OK
+
+                                       if ($conn->{state} eq 'WC') {
+                                               $SendMsg->Call($Stream[$i], "?\r", 2); # Trigger response for chat script
+                                       }
+
+                                       # Just ignore incomming connect if we think it is already connected
+
+                               } else {
+
+                                       # New Incoming Connect
+
+                                       $conn = BPQMsg->new($rproc);
+                                       $conn->{bpqstream} = $Stream[$i];
+                                       $conn->{lineend} = "\cM";
+                                       $conn->{incoming} = 1;
+                                       $conn->{bpqcall} = $call;
+                                       $circuit{$Stream[$i]} = $conn;
+                                       if (my ($c, $s) = $call =~ /^(\w+)-(\d\d?)$/) {
+                                               $s = 15 - $s if $s > 8;
+                                               $call = $s > 0 ? "${c}-${s}" : $c;
+                                       }
+                                       $conn->to_connected($call, 'A', $conn->{csort} = 'ax25');
+                               }
+
+                       }
+
+               }
+
+               # See if data received
+
+               my $cnt = $RXCount->Call($Stream[$i]);
+
+               while ($cnt > 0) {
+                       $cnt--;
+
+                       my $Buffer = " " x 340;
+
+                       my $len=0;
+
+                       $len=$GetMsg->Call($Stream[$i],$Buffer);
+
+                       $Buffer = substr($Buffer,0,$len);
+
+                       dbg ("BPQ RX: $Buffer") if isdbg('bpq');
+
+                       my $conn = $circuit{$Stream[$i]};
+
+                       if ($conn) {
+
+                               dbg("BPQ State = $conn->{state}") if isdbg('bpq');
+
+                               if ($conn->{state} eq 'WC') {
+                                       if (exists $conn->{cmd}) {
+                                               if (@{$conn->{cmd}}) {
+                                                       dbg($Buffer) if isdbg('connect');
+                                                       $conn->_docmd($Buffer);
+                                               }
+                                       }
+                                       if ($conn->{state} eq 'WC' && exists $conn->{cmd} && @{$conn->{cmd}} == 0) {
+                                               $conn->to_connected($conn->{call}, 'O', $conn->{csort});
+                                       }
+                               } else {
+                                       my @lines = split /\cM\cJ?/, $Buffer;
+                                       push @lines, $Buffer unless @lines;
+                                       for (@lines) {
+                                               &{$conn->{rproc}}($conn, "I$conn->{call}|$_");
+                                       }
+                               }
+                       } else {
+                               dbg("BPQ error Unsolicited Data!");
+                       }
+               }
+       }
+}
+
+1;
+