#!/usr/bin/env perl # # TestTarget_LVRT - how to manage the test environment on a LabVIEW RT target. # # We can FTP files to and from the LabVIEW target, but there's no NFS or # SMB shares. # Most information about the target itself is specified via environment # variables. Environment variables with settings are named using the target's # config name with a specific suffix. The current environment variables are: # _IPNAME - the host name/IP of the target. # _CTLPORT- the TCP port number to connect to for the test # controller. If this is not set, port 8888 is used. # _FSROOT - the root of the filesystem on the target where # ACE files will be created from (cwd, if you will). # If this is not set, "\ni-rt" is used as the root. # # Each of these settings are stored in a member variable of the same name in # each object. The process objects can access them using, e.g., # $self->{TARGET}->{IPNAME}. # # This class also makes an FTP object available to process objects that are # created. FTP is set up before creating a process object and can be used to # transfer files to and from the LVRT target. package PerlACE::TestTarget_LVRT; our @ISA = "PerlACE::TestTarget"; ### Constructor and Destructor sub new { my $proto = shift; my $config_name = shift; my $class = ref ($proto) || $proto; my $self = {}; bless ($self, $class); $self->GetConfigSettings($config_name); my $targethost; my $env_name = $config_name.'_IPNAME'; if (exists $ENV{$env_name}) { $targethost = $ENV{$env_name}; } else { print STDERR "You must define target hostname/IP with $env_name\n"; undef $self; return undef; } $env_name = $config_name.'_CTLPORT'; if (exists $ENV{$env_name}) { $self->{CTLPORT} = $ENV{$env_name}; } else { print STDERR "Warning: no $env_name variable; falling back to ", "port 8888\n"; $self->{CTLPORT} = 8888; } $env_name = $config_name.'_FSROOT'; my $fsroot = '\\ni-rt\\system'; if (exists $ENV{$env_name}) { $fsroot = $ENV{$env_name}; } else { print STDERR "Warning: no $env_name variable; falling back ", "to $fsroot\n"; } $self->{FSROOT} = $fsroot; $self->{REBOOT_CMD} = $ENV{'ACE_REBOOT_LVRT_CMD'}; if (!defined $self->{REBOOT_CMD}) { $self->{REBOOT_CMD} = 'I_Need_A_Reboot_Command'; } $self->{REBOOT_TIME} = $ENV{'ACE_LVRT_REBOOT_TIME'}; if (!defined $self->{REBOOT_TIME}) { $self->{REBOOT_TIME} = 200; } $self->{REBOOT_TIME} = $ENV{'ACE_RUN_LVRT_REBOOT_TIME'}; if (!defined $self->{REBOOT_TIME}) { $self->{REBOOT_TIME} = 200; } $self->{REBOOT_NEEDED} = undef; $self->{FTP} = new Net::FTP ($targethost); $self->{IPNAME} = $targethost; if (!defined $self->{FTP}) { print STDERR "Error opening FTP to $targethost: $@\n"; $self->{REBOOT_NEEDED} = 1; undef $self; return undef; } $self->{FTP}->login("",""); return $self; } sub DESTROY { my $self = shift; # Reboot if needed; set up clean for the next test. if (defined $self->{REBOOT_NEEDED} && $self->{REBOOT_CMD}) { $self->RebootNow; } # See if there's a log; should be able to retrieve it from rebooted target. if (defined $ENV{'ACE_TEST_VERBOSE'}) { print STDERR "LVRT target checking for remaining log...\n"; } $self->GetStderrLog(); if (defined $self->{FTP}) { $self->{FTP}->close; $self->{FTP} = undef; } } ################################################################## sub LocalFile ($) { my $self = shift; my $file = shift; my $newfile = $self->{FSROOT} . '\\' . $file; print STDERR "LVRT LocalFile for $file is $newfile\n"; return $newfile; } sub DeleteFile ($) { my $self = shift; $self->{FTP}->login("",""); foreach my $file (@_) { my $newfile = $self->LocalFile($file); $self->{FTP}->delete($newfile); } } sub GetFile ($) { # Use FTP to retrieve the file from the target; should still be open. # If only one name is given, use it for both local and remote (after # properly LocalFile-ing it). If both names are given, assume the caller # knows what he wants and don't adjust the paths. my $self = shift; my $remote_file = shift; my $local_file = shift; if (!defined $local_file) { $local_file = $remote_file; $remote_file = $self->LocalFile($local_file); } $self->{FTP}->ascii(); if ($self->{FTP}->get($remote_file, $local_file)) { return 0; } return -1; } sub WaitForFileTimed ($) { my $self = shift; my $file = shift; my $timeout = shift; my $newfile = $self->LocalFile($file); my $targetport = $self->{CTLPORT}; my $target = new Net::Telnet(Errmode => 'return'); if (!$target->open(Host => $self->{IPNAME}, Port => $targetport)) { print STDERR "ERROR: target $self->{IPNAME}:$targetport: ", $target->errmsg(), "\n"; return -1; } my $cmdline = "waitforfile $newfile $timeout"; if (defined $ENV{'ACE_TEST_VERBOSE'}) { print "-> $cmdline\n"; } $target->print("$cmdline"); my $reply; # Add a small comms delay factor to the timeout $timeout = $timeout + 2; $reply = $target->getline(Timeout => $timeout); if (defined $ENV{'ACE_TEST_VERBOSE'}) { print "<- $reply\n"; } $target->close(); if ($reply eq "OK\n") { return 0; } return -1; } sub CreateProcess ($) { my $self = shift; my $process = new PerlACE::ProcessLVRT ($self, @_); return $process; } sub GetStderrLog ($) { my $self = shift; # Tell the target to snapshot the stderr log; if there is one, copy # it up here and put it out to our stderr. my $targetport = $self->{CTLPORT}; my $target = new Net::Telnet(Errmode => 'return'); if (!$target->open(Host => $self->{IPNAME}, Port => $targetport)) { print STDERR "ERROR: target $self->{IPNAME}:$targetport: ", $target->errmsg(), "\n"; return; } my $cmdline = "snaplog"; if (defined $ENV{'ACE_TEST_VERBOSE'}) { print "-> $cmdline\n"; } $target->print("$cmdline"); my $reply; $reply = $target->getline(); if (defined $ENV{'ACE_TEST_VERBOSE'}) { print "<- $reply\n"; } $target->close(); if ($reply eq "NONE\n") { return; } chomp $reply; if (undef $self->{FTP}) { $self->{FTP} = new Net::FTP ($self->{IPNAME}); if (!defined $self->{FTP}) { print STDERR "$@\n"; return -1; } $self->{FTP}->login("",""); } $self->{FTP}->ascii(); if ($self->{FTP}->get($reply, "stderr.txt")) { $self->{FTP}->delete($reply); open(LOG, "stderr.txt"); while () { print STDERR; } close LOG; unlink "stderr.txt"; } return; } # Copy a file to the target. Adjust for different types (DLL, EXE, TEXT) # and debug/non (for DLLs). Additionally, a file can be removed when this # object is deleted, or left in place. sub NeedFile ($) { my $self = shift; } # Need a reboot when this target is destroyed. sub NeedReboot ($) { my $self = shift; $self->{REBOOT_NEEDED} = 1; } # Reboot target sub RebootNow ($) { my $self = shift; $self->{REBOOT_NEEDED} = undef; print STDERR "Attempting to reboot target...\n"; if (defined $self->{FTP}) { $self->{FTP}->close; $self->{FTP} = undef; } system ($self->{REBOOT_CMD}); sleep ($self->{REBOOT_TIME}); } # Reboot now then try to restore the FTP connection. sub RebootReset ($) { my $self = shift; $self->RebootNow; my $targethost = $self->{IPNAME}; $self->{FTP} = new Net::FTP ($targethost); if (!defined $self->{FTP}) { print STDERR "Error reestablishing FTP to $targethost: $@\n"; } else { $self->{FTP}->login("",""); } } sub KillAll ($) { my $self = shift; my $procmask = shift; PerlACE::ProcessLVRT::kill_all ($procmask, $self); } 1;