################################################################################ package MGPIB; ################################################################################ # Provides a class for communicating with a GPIB server # by Michael Siegenthaler ( www.msigi.net ) # June 16, 2005 ################################################################################ use strict; use Carp; use IO::Socket; ################################################################################ # CONFIGURATION SECTION my $config_server = "lab.sr.msigi.net"; my $config_port = 850; my $config_address = 7; # END CONFIGURATION SECTION ################################################################################ # Class variables my $object_count = 0; # number of instances in existance ################################################################################ return 1; # package loaded successfully ################################################################################ ##doc ---------------------------------------------------------------------- ##doc General Information ##doc Functions return 1 of successful and undef on error ##doc When an error occurs, it sets an error flag and error string. If the ##doc error flag is set, all subsequent calls will return an error and will ##doc not execute. This makes it possible to chain together a whole bunch ##doc of commands in sequence, without bothering to check for errors. At ##doc critical points, check_error() should be called to see whether all ##doc operations so far were successful. ##doc ################################################################################ # PUBLIC METHODS ################################################################################ ##doc ---------------------------------------------------------------------- ##doc new() ##doc Object creation ##doc Syntax: my $g = MGPIB->new( server => 'hostname', ##doc port => #, ##doc address => # ); ##doc sub new() { my $class = shift; my %params = @_; my $self = { }; # Configure server hostname if(defined $params{'server'}) { $self->{config_server} = $params{'server'}; } else { $self->{config_server} = $config_server; } # Configure server port if(defined $params{'port'}) { $self->{config_port} = $params{'port'}; } else { $self->{config_port} = $config_port; } # Configure GPIB address if(defined $params{'address'}) { $self->{config_address} = $params{'address'}; } else { $self->{config_address} = $config_address; } # Initialize some variables $self->{welcome} = ""; $self->{is_connected} = 0; $self->{is_locked} = 0; $self->{has_error} = 0; $self->{error_number} = 0; $self->{error_string} = "No error"; bless $self, $class; $self->_initialize(); # call constructor return $self; } # end new() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc get_server() ##doc Return server hostname ##doc sub get_server { my $self = shift; return $self->{config_server}; } # end get_server() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc get_port() ##doc Return server port ##doc sub get_port { my $self = shift; return $self->{config_port}; } # end get_port() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc get_address() ##doc Return GPIB address ##doc sub get_address { my $self = shift; return $self->{config_address}; } # end get_address() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc get_welcome() ##doc Return welcome message given by proxy server ##doc sub get_welcome { my $self = shift; return $self->{welcome}; } # end get_welcome() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc set_address(#) ##doc Set GPIB address ##doc Instrument must not be locked in order to do this ##doc sub set_address { my $self = shift; my $newaddr = shift; if ($self->{is_locked}) { my $oldaddr = $self->{config_address}; $self->{has_error} = 1; $self->{error_number} = 60; $self->{error_string} = "Address change from $oldaddr to " . "$newaddr while instrument was locked."; return; # Cannot change address while locked } else { $self->{config_address} = $newaddr; } return 1; } # end set_address() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc connect() ##doc Connect to GPIB server ##doc sub connect { my $self = shift; if ($self->{has_error}) { return; } my $sock = IO::Socket::INET->new(PeerAddr => $self->{config_server}, PeerPort => $self->{config_port}, Proto => "tcp", Type => SOCK_STREAM); if (!defined($sock) || !($sock)) { $self->{has_error} = 1; $self->{error_number} = 61; $self->{error_string} = "connect()"; return; } $self->{is_connected} = 1; # set flag $self->{socket} = $sock; # save socket for later use $self->{welcome} = <$sock>; # read one line if (!defined($self->{welcome})) { $self->{has_error} = 1; $self->{error_number} = 63; $self->{error_string} = "connect() welcome"; $self->close(); return; } $self->{welcome} =~ s/\s*$//s; # trim trailing newline return 1; } # end connect() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc init() ##doc Initialize instrument ##doc This will call connect() automatically if necessary ##doc sub init { my $self = shift; # Connect if not already connected if (!($self->{is_connected})) { # need to connect $self->connect(); } if ($self->{has_error}) { return; } my $sock = $self->{socket}; my $address = $self->{config_address}; # Lock the instrument if ($self->{is_locked}) { # if locked, we have already init'ed and don't need to again return 1; } my $retval = print $sock "init $address\r\n"; if (!defined($retval) || !($retval)) { $self->{has_error} = 1; $self->{error_number} = 62; $self->{error_string} = "init()"; $self->close(); return; } my $reply = <$sock>; if (!defined($reply)) { $self->{has_error} = 1; $self->{error_number} = 63; $self->{error_string} = "init()"; $self->close(); return; } $reply =~ s/\s*$//s; # trim trailing newline if ($reply eq "ok") { # Success! $self->{is_locked} = 1; return 1; } # If we get here, something must have gone wrong if ($reply =~ /^error\s+\d+/) { # A properly formatted error occured $reply =~ s/^error\s+//; # remove "error " from start of string my $errnum = $reply; $errnum =~ s/\D.*//; # remove first nondigit and everything that follows my $errtext = $reply; $errtext =~ s/^\d*\s*//; # remove leading digits and whitespace $self->{has_error} = 1; $self->{error_number} = $errnum; $self->{error_string} = "init(): $errtext"; return; } $self->{has_error} = 1; $self->{error_number} = 64; $self->{error_string} = "init()"; $self->close(); return; } # end init() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc free() ##doc Free an instrument ##doc Stops using an instrument but does not disconnect ##doc sub free { my $self = shift; if ($self->{has_error}) { return; } my $sock = $self->{socket}; if ($self->{is_locked}) { my $retval = print $sock "free\r\n"; if (!defined($retval) || !($retval)) { $self->{has_error} = 1; $self->{error_number} = 62; $self->{error_string} = "free()"; $self->close(); return; } my $reply = <$sock>; if (!defined($reply)) { $self->{has_error} = 1; $self->{error_number} = 63; $self->{error_string} = "free()"; $self->close(); return; } $reply =~ s/\s*$//s; # trim trailing newline if ($reply eq "ok") { # Success! $self->{is_locked} = 0; return 1; } # If we get here, something must have gone wrong # There should never be an error on freeing an instrument, so assume the # worst and close the connection $self->{has_error} = 1; $self->{error_number} = 64; $self->{error_string} = "free()"; $self->close(); return; } # Not locked, so instrument is already free return 1; } # end free() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc write("string") ##doc Write string to instrument ##doc sub write { my $self = shift; my $buf = shift; if (!($self->{is_locked})) { # Instrument not yet locked $self->init(); } if ($self->{has_error}) { return; } my $sock = $self->{socket}; # Send the string my $retval = print $sock "write $buf\r\n"; if (!defined($retval) || !($retval)) { $self->{has_error} = 1; $self->{error_number} = 62; $self->{error_string} = "write()"; $self->close(); return; } my $reply = <$sock>; if (!defined($reply)) { $self->{has_error} = 1; $self->{error_number} = 63; $self->{error_string} = "write()"; $self->close(); return; } $reply =~ s/\s*$//s; # trim trailing newline if ($reply eq "ok") { # Success! return 1; } # If we get here, something must have gone wrong if ($reply =~ /^error\s+\d+/) { # A properly formatted error occured $reply =~ s/^error\s+//; # remove "error " from start of string my $errnum = $reply; $errnum =~ s/\D.*//; # remove first nondigit and everything that follows my $errtext = $reply; $errtext =~ s/^\d*\s*//; # remove leading digits and whitespace $self->{has_error} = 1; $self->{error_number} = $errnum; $self->{error_string} = "write(): $errtext"; return; } $self->{has_error} = 1; $self->{error_number} = 64; $self->{error_string} = "write()"; $self->close(); return; } # end write() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc read() ##doc Read string from instrument ##doc sub read { my $self = shift; if (!($self->{is_locked})) { # Instrument not yet locked $self->init(); } if ($self->{has_error}) { return; } my $sock = $self->{socket}; # Send read request my $retval = print $sock "read\r\n"; if (!defined($retval) || !($retval)) { $self->{has_error} = 1; $self->{error_number} = 62; $self->{error_string} = "read()"; $self->close(); return; } my $reply = <$sock>; if (!defined($reply)) { $self->{has_error} = 1; $self->{error_number} = 63; $self->{error_string} = "read()"; $self->close(); return; } $reply =~ s/\s*$//s; # trim trailing newline if ($reply =~ /^read\s/s) { # Success! $reply =~ s/^read\s*//s; return $reply; } # If we get here, something must have gone wrong if ($reply =~ /^error\s+\d+/) { # A properly formatted error occured $reply =~ s/^error\s+//; # remove "error " from start of string my $errnum = $reply; $errnum =~ s/\D.*//; # remove first nondigit and everything that follows my $errtext = $reply; $errtext =~ s/^\d*\s*//; # remove leading digits and whitespace $self->{has_error} = 1; $self->{error_number} = $errnum; $self->{error_string} = "read(): $errtext"; return; } $self->{has_error} = 1; $self->{error_number} = 64; $self->{error_string} = "read()"; $self->close(); return; } # end read() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc query("string") ##doc Query instrument ##doc sub query { my $self = shift; my $buf = shift; if (!($self->{is_locked})) { # No instrument is locked yet $self->init(); } if ($self->{has_error}) { return; } my $sock = $self->{socket}; # Send the string my $retval = print $sock "query $buf\r\n"; if (!defined($retval) || !($retval)) { $self->{has_error} = 1; $self->{error_number} = 62; $self->{error_string} = "query()"; $self->close(); return; } my $reply = <$sock>; if (!defined($reply)) { $self->{has_error} = 1; $self->{error_number} = 63; $self->{error_string} = "query()"; $self->close(); return; } $reply =~ s/\s*$//s; # trim trailing newline if ($reply =~ /^read\s/s) { # Success! $reply =~ s/^read\s*//s; return $reply; } # If we get here, something must have gone wrong if ($reply =~ /^error\s+\d+/) { # A properly formatted error occured $reply =~ s/^error\s+//; # remove "error " from start of string my $errnum = $reply; $errnum =~ s/\D.*//; # remove first nondigit and everything that follows my $errtext = $reply; $errtext =~ s/^\d*\s*//; # remove leading digits and whitespace $self->{has_error} = 1; $self->{error_number} = $errnum; $self->{error_string} = "query(): $errtext"; return; } $self->{has_error} = 1; $self->{error_number} = 64; $self->{error_string} = "query()"; $self->close(); return; } # end query() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc close() ##doc Close connection ##doc Unlocks instrument and closes the connection ##doc sub close { my $self = shift; my $sock = $self->{socket}; if ($self->{is_connected}) { print $sock "close\r\n"; # if this fails we don't care $self->{is_connected} = 0; $self->{is_locked} = 0; close($sock); } if ($self->{has_error}) { return; } return 1; } # end close() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc check_error() ##doc Checks error ##doc Returns 1 if there has been an error, 0 otherwise ##doc sub check_error { my $self = shift; if ($self->{has_error}) { return 1; # error } return; # no error } # end check_error() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc get_error() ##doc Get error ##doc Returns a detailed string describing the error ##doc sub get_error { my $self = shift; my $errnum = $self->{error_number}; my $errtypestr = $self->lookup_error($self->{error_number}); my $errlocstr = $self->{error_string}; my $errstr = "ERROR $errnum: $errtypestr: $errlocstr"; return $errstr; } # end get_error() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc die_error() ##doc Die on error ##doc Checks whether an error has occured, and dies if so ##doc sub die_error { my $self = shift; if ($self->check_error()) { my $errstr = $self->get_error(); croak $errstr; } return 1; } # end die_error() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc lookup_error(#) ##doc Lookup error ##doc Returns a string for a particular error number ##doc sub lookup_error { my $self = shift; my $errnum = shift; if ($errnum==60) { return "Paramter updated failed."; } elsif ($errnum==61) { return "Cannot open connection"; } elsif ($errnum==62) { return "Cannot write to socket"; } elsif ($errnum==63) { return "Cannot read from socket"; } elsif ($errnum==64) { return "Protocol violation"; } elsif ($errnum==31) { return "Server said bad command"; } elsif ($errnum==32) { return "GPIB communications error"; } elsif ($errnum==33) { return "Fatal error"; } elsif ($errnum==34) { return "Resource unavailable"; } elsif ($errnum==35) { return "Server has internal error"; } else { return ""; } } # end lookup_error() ################################################################################ ################################################################################ ##doc ---------------------------------------------------------------------- ##doc clear_error() ##doc Clear error ##doc Resets the error flag and error string ##doc sub clear_error { my $self = shift; $self->{has_error} = 0; $self->{error_number} = 0; $self->{error_string} = "No error"; return 1; } # end clear_error() ################################################################################ ################################################################################ # END PUBLIC METHODS ################################################################################ # PRIVATE METHODS # The following methods should not be called from outside the package ################################################################################ # Object constructor sub _initialize { my $self = shift; if(!$object_count) { # this is the first instance } $object_count++; } ################################################################################ ################################################################################ # Object destructor sub DESTROY { my $self = shift; $self->close(); $object_count--; if(!$object_count) { # this is the last instance } } # end DESTROY() ################################################################################