From ca3138e6171008cef74c62652b4bbbd01764a9c6 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 10:34:14 +0100 Subject: [PATCH 01/16] Adding capr.pl --- src/cap5/capr.pl | 419 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) create mode 100644 src/cap5/capr.pl diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl new file mode 100644 index 000000000..f78741412 --- /dev/null +++ b/src/cap5/capr.pl @@ -0,0 +1,419 @@ +#!/usr/bin/perl -w + +####################################################################### +# +# capr: A program that attempts to do a "dbpr" command via channel +# access. +# +####################################################################### + +use strict; +use Getopt::Std; +use lib "/dls_sw/epics/R3.14.11/base/lib/perl"; +use CA; + +######### Globals ########## + +# Non APS users will want to modify theDbdFile default settings + +my $theDbdFile = "/net/helios/iocapps/R3.13.9/ioc/linac/2/dbd/linac.dbd"; + +my $hostArch; +if ( defined $ENV{"EPICS_HOST_ARCH"} ) { + $hostArch = $ENV{"EPICS_HOST_ARCH"} ; +} else { + $hostArch="solaris-sparc"; +} + +our( $opt_d, $opt_f, $opt_r); +my $usage = "Usage:\n $0 [-d dbd_file] ( [-f record_type] | [-r] | [record_name] ) [interest]\n"; +my %record = (); # Empty hash to put dbd data in +my $iIdx = 0; # Array indexes for interest, data type and base +my $tIdx = 1; +my $bIdx = 2; +my %device = (); # Empty hash to record which rec types have device support +my $DEBUG=0; # DEBUG + +# EPICS field types referenced to their equivalent EZCA types +my %fieldType = ( + DBF_STRING => "ezcaString", + DBF_BYTE => "ezcaByte", + DBF_CHAR => "ezcaByte", + DBF_UCHAR => "ezcaChar", + DBF_SHORT => "ezcaShort", + DBF_USHORT => "ezcaLong", + DBF_LONG => "ezcaLong", + DBF_ULONG => "ezcaDouble", + DBF_FLOAT => "ezcaFloat", + DBF_DOUBLE => "ezcaDouble", + DBF_ENUM => "ezcaString", + DBF_MENU => "ezcaString", + DBF_DEVICE => "ezcaString", + DBF_INLINK => "ezcaString", + DBF_OUTLINK => "ezcaString", + DBF_FWDLINK => "ezcaString", + DBF_NOACCESS => "ezcaNoAccess" +); + + +######### Main program ############ + +getopts('d:f:r') or die "$usage"; + +# Select the dbd file to use +if($opt_d) { # command line has highest priority + $theDbdFile = $opt_d; +} +elsif (exists $ENV{CAPR_DBD_FILE}) { # Use the env var if it exists + $theDbdFile = $ENV{CAPR_DBD_FILE}; +} # Otherwise use the default set above + +parseDbd($theDbdFile); +print "Using $theDbdFile\n\n"; + +# Print a list of record types +if($opt_r) { + print ("Record types defined in $theDbdFile\n"); + printList(0); + exit; +} + +# Print the fields defined for given record +if($opt_f) { + printRecordList($opt_f); + exit; +} +#my @data; +#@data = EZCA::Get("S:BM:TMDispAI.VAL", "ezcaDouble",1); +#if( $data[0] ) { die "ca get error on field --" }; +#print(" $data[1] "); +#die "end"; + +# Do the business +# Allow commas between arguments as in vxWorks dbpr +die "$usage" unless defined $ARGV[0]; +$ARGV[0] =~ s/,/ /; # Get rid of pesky comma if it's there +if($ARGV[0] =~ m/\s+\d/) { # If we replace comma with a space, + ($ARGV[0], $ARGV[1]) = split(/ /, $ARGV[0]); #split it +} +$ARGV[0] =~ s/\s+//; # Remove any spaces +$ARGV[0] =~ s/\..*//; # Get rid of field name if it's there +$ARGV[1] =~ s/\D//g; # Make sure we only use digits +$ARGV[1] = $ARGV[1] || 0; # interest defaults to 0 +printRecord($ARGV[0], $ARGV[1]); # Do the do + + +########## End of main ########### + + + +# parseDbd +# takes given dbd file and parses it to produce a hash table of record types +# giving their fields, and for each field its interest level and data type +# usage: void parseDbd("fileName"); +# Output is in the hash %record. This is a hash of (references to) another. +# hash containing the fields of this record, as keys. The value keyed by +# the field names are (references to) arrays. Each of these arrays contains +# the interest level, data type and base of the field +sub parseDbd { + my $dbdFile = $_[0]; + my @dbd; + my $length; + my $level = 0; + my $i = 0; + my $isArecord = 0; + my $isAfield; + my $thisRecord; + my $thisField; + my $thisType; + my %field = (); + my @params = (); + my $interest = 0; + my $thisBase = "DECIMAL"; + my $item; + my $newDevice; + + open(DBD, "< $dbdFile") || die "Can't open dbd file $dbdFile --"; + @dbd = ; + $length = @dbd; + close(DBD) || die "Can't close $dbdFile --"; + + while ($i < $length) { + $_ = $dbd[$i]; + chomp; + print("line $i - level $level\n") if ($DEBUG); + #$line = $dbd[$i] || die "Unexpected end of file: $dbdFile, line $."; + if( m/recordtype/ ) { + ($level == 0) || die "dbd file format error in or before line $i --"; + m/\((.*)\)/; #get record type + #@records = (@records, $1); + $isArecord = 1; + $thisRecord = $1; + } + if( m/field/ ) { + ($level == 1 && $isArecord) || die "dbd file format error in or before line $i --"; + m/\((.*),/; # get field name + $thisField = $1; + m/,(.*)\)/; # get field type + $thisType = $1; + $isAfield = 1; + #print("$1 , line $i "); + } + if( m/interest/ ) { + ($level == 2 && $isAfield) || die "dbd file format error in or before line $i --"; + m/\((.*)\)/ ; # get interest level, default = 0 + $interest = $1; + } + if( m/base/ ) { + ($level == 2 && $isAfield) || die "dbd file format error in or before line $i --"; + m/\((.*)\)/ ; # get base, default = DECIMAL + $thisBase = $1; + } + if( m/\{/ ) { $level++ }; + if( m/\}/ ) { + if( $level == 2 && $isAfield) { + $isAfield = 0; + $params[$iIdx] = $interest; + $params[$tIdx] = $thisType; + $params[$bIdx] = $thisBase; + $field{$thisField} = [@params]; + #print("interest $interest\n"); + $interest = 0; # set up default for next time + $thisBase = "DECIMAL"; # set up default for next time + } + if( $level == 1 && $isArecord) { + $isArecord = 0; + $record{$thisRecord} = { %field }; + #print("record type $thisRecord "); + #foreach $key (keys(%field)) { + # print("Field $key - interest $field{$key}\n"); + #} + %field = (); # set up for next time + } + $level--; + } + # Parse for record types with device support + if( m/device/ ) { + m/\((.*?),/; + if(!exists($device{$1})) { + # Use a hash to make a list of record types with device support + $device{$1} = 1; + } + } + $i++; + } +} + + +# Given a record name attempts to find the record and its type. +# Usage: getRecType(recordName) - returns ($error, $recordType) +sub getRecType { + my $name = $_[0] . ".RTYP"; + my $type; + my $data; + + $data = caget($name); + if ($data =~ m/Invalid channel name/) { die "Record \"$_[0]\" not found\n"; } + chomp $data; + $data =~ s/\s+//; + #print("$name is a \"$data\"type\n"); + return($data); +} + +# Given the record type and the field returns the interest level, data type +# and base for the field +# Usage: ($dataType, $interest, $base) getFieldParams( $recType, $field) +sub getFieldParams { + my $recType = $_[0]; + my $field = $_[1]; + my ($fType, $fInterest, $fBase); + + exists($fieldType{$record{$recType}{$field}[$tIdx]}) || + die "Field data type $field for $recType not found in dbd file --"; + exists($record{$recType}{$field}[$iIdx]) || + die "Interest level for $field in $recType not found in dbd file --"; + + $fType = $fieldType{$record{$recType}{$field}[$tIdx]}; + $fInterest = $record{$recType}{$field}[$iIdx]; + $fBase = $record{$recType}{$field}[$bIdx]; + #print("getFieldParams: $recType, $field, $fType, $fInterest\n"); + return($fType, $fInterest, $fBase); +} + +# Prints field name and data for given field. Formats output so +# that fields align in to 4 columns. Tries to imitate dbpf format +# Usage: printField( $fieldName, $data, $dataType, $base, $firstColumnPosn) +sub printField { + my $fieldName = $_[0]; + my $fieldData = $_[1]; + my $dataType = $_[2]; + my $base = $_[3]; # base to display numeric data in + my $col = $_[4]; # first column to print in + + my $screenWidth = 80; + my ($outStr, $len, $wide, $pad, $field); + + $field = $fieldName . ":"; + + if( $dataType eq "ezcaString" ) { + $outStr = sprintf("%-5s %s", $field, $fieldData); + } elsif ( $base eq "HEX" ) { + $outStr = sprintf("%-5s %x", $field, $fieldData); + } elsif ( $dataType eq "ezcaDouble" || $dataType eq "ezcaFloat" ) { + $outStr = sprintf("%-5s %.8f", $field, $fieldData); + } elsif ( $dataType eq "ezcaShort" ) { + $outStr = sprintf("%-5s %d", $field, $fieldData); + } elsif ( $dataType eq "ezcaChar" ) { + $outStr = sprintf("%-5s %d", $field, ord($fieldData)); + }else { + $outStr = sprintf("%-5s %d", $field, $fieldData); + } + + $len = length($outStr); + if($len <= 20) { $wide = 20; } + elsif( $len <= 40 ) { $wide = 40; } + elsif( $len <= 60 ) { $wide = 60; } + else { $wide = 80;} + + $pad = $wide - $len; + #print "outStr=$outStr .. pad=$pad\n"; + + if( $col + $wide > $screenWidth ) { + print("\n"); + $col = 0; + } + + print sprintf("$outStr%*s",$pad," "); + $col = $col + $wide; + + return($col); +} + +# Given record name and interest level prints data from record fields +# that are at or below the interest level specified. +# Useage: printRecord( $recordName, $interestLevel) +sub printRecord { + my $name = $_[0]; + my $interest = $_[1]; + my ($error, $recType, $field, $fType, $fInterest, $data); + my ($fToGet, $col, $base); + #print("checking record $name, interest $interest\n"); + $col = 0; + + $recType = getRecType($name); + print("$name is record type $recType\n"); + exists($record{$recType}) || die "Record type $recType not found in dbd file --"; + + foreach $field (sort keys %{$record{$recType}}) { + # Skip DTYP field if this rec type doesn't have device support defined + if($field eq "DTYP" && !(exists($device{$recType}))) { next; } + + ($fType, $fInterest, $base) = getFieldParams($recType, $field); + unless( $fType eq "ezcaNoAccess" ) { + if( $interest >= $fInterest ) { + $fToGet = $name . "." . $field; + $data = caget($fToGet); + chomp $data; + #$data =~ s/\s+//; + #if( $data[0] ) { die "ca get error on field $field --" }; + $col = printField($field, $data, $fType, $base, $col); + #print ("fType = $fType $data[1]\n"); + } + } + } + print("\n"); # Final line feed +} + +# Prints list of record types found in dbd file. If level > 0 +# then the fields of that record type, their interest levels and types are +# also printed. +# Diagnostic routine, usage: void printList(level); +sub printList { + my $level = $_[0]; + my ($rkey, $fkey); + + foreach $rkey (sort keys(%record)) { + print("$rkey\n"); + if($level > 0) { + foreach $fkey (keys %{$record{$rkey}}) { + print("\tField $fkey - interest $record{$rkey}{$fkey}[$iIdx] "); + print("- type $record{$rkey}{$fkey}[$tIdx] "); + print("- base $record{$rkey}{$fkey}[$bIdx]\n"); + } + } + } +} + +# Prints list of fields with interest levels for given record type +# Diagnostic routine, usage: void printRecordList("recordType"); +sub printRecordList { + my ($rkey, $fkey); + my $type = $_[0]; + + if( exists($record{$type}) ) { + print("Record type - $type\n"); + foreach $fkey (sort keys %{$record{$type}}) { + printf("%-4s", $fkey); + printf(" interest = $record{$type}{$fkey}[$iIdx]"); + printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]); + print (" base = $record{$type}{$fkey}[$bIdx]\n"); + } + } + else { + print("Record type $type not defined in dbd file $theDbdFile\n"); + } +} + +# variable to communicate callback handler result +my $callback_data; +my $incomplete; +my $cadebug = 0; + +# returns a terse result of a caget operation +# using perlCA +sub caget { + my $name = $_[0]; + print $name . "\n" if $cadebug; + show_call_stack() if $cadebug; + my $channel = CA->new($name); + my $wait = 1; + eval { CA->pend_io($wait); }; + if ($@) { + if ($@ =~ m/^ECA_TIMEOUT/) { + my $err = "'" . $channel . "'"; + print "Channel connect timed out: $err not found.\n"; + } + die $@; + } + my $type = $channel->field_type; + my $count = $channel->element_count; + $channel->get_callback(\&callback_handler, $type); + $incomplete = 1; + CA->pend_event(0.1) while ($incomplete); + return $callback_data; +} + +sub callback_handler +{ + my ($chan, $status, $data) = @_; + die $status if $status; + #display($chan, $type, $data); + $callback_data = $data; + $incomplete=0; +} + +sub show_call_stack { + my ( $path, $line, $subr ); + my $max_depth = 30; + my $i = 1; + print("--- Begin stack trace ---\n"); + my @call_details; + @call_details = caller($i); + while ( (@call_details) && ($i<$max_depth) ) { + print("$call_details[1] line $call_details[2] in function $call_details[3]\n"); + $i = $i +1; + @call_details = caller($i); + } + print("--- End stack trace ---\n"); +} + From 99605c85fa6a6ffed0c5debd316cc47fa2fb149c Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 11:14:50 +0100 Subject: [PATCH 02/16] relocating library refs within base --- src/cap5/capr.pl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index f78741412..672fd509d 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -8,8 +8,11 @@ ####################################################################### use strict; + +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + use Getopt::Std; -use lib "/dls_sw/epics/R3.14.11/base/lib/perl"; use CA; ######### Globals ########## @@ -83,11 +86,6 @@ if($opt_f) { printRecordList($opt_f); exit; } -#my @data; -#@data = EZCA::Get("S:BM:TMDispAI.VAL", "ezcaDouble",1); -#if( $data[0] ) { die "ca get error on field --" }; -#print(" $data[1] "); -#die "end"; # Do the business # Allow commas between arguments as in vxWorks dbpr From 797d263f0469fcfbd560ba1f492651cbd63de9ed Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 21:00:43 +0100 Subject: [PATCH 03/16] Comment removed --- src/cap5/capr.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 672fd509d..5c999eda3 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -234,7 +234,6 @@ sub getFieldParams { $fType = $fieldType{$record{$recType}{$field}[$tIdx]}; $fInterest = $record{$recType}{$field}[$iIdx]; $fBase = $record{$recType}{$field}[$bIdx]; - #print("getFieldParams: $recType, $field, $fType, $fInterest\n"); return($fType, $fInterest, $fBase); } From 408721daec68c69cf6e38b7ee4d4bb5b426424f7 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 21:44:38 +0100 Subject: [PATCH 04/16] Install capr.pl Parallel caget --- src/cap5/Makefile | 1 + src/cap5/capr.pl | 96 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/cap5/Makefile b/src/cap5/Makefile index ec2330607..05f3de1e3 100644 --- a/src/cap5/Makefile +++ b/src/cap5/Makefile @@ -29,6 +29,7 @@ ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) PERL_SCRIPTS += cainfo.pl PERL_SCRIPTS += caput.pl PERL_SCRIPTS += caget.pl + PERL_SCRIPTS += capr.pl PERL_SCRIPTS += camonitor.pl PERL_MODULES += CA.pm diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 5c999eda3..8986cf1f4 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -31,7 +31,7 @@ if ( defined $ENV{"EPICS_HOST_ARCH"} ) { our( $opt_d, $opt_f, $opt_r); my $usage = "Usage:\n $0 [-d dbd_file] ( [-f record_type] | [-r] | [record_name] ) [interest]\n"; my %record = (); # Empty hash to put dbd data in -my $iIdx = 0; # Array indexes for interest, data type and base +my $iIdx = 0; # Array indexes for interest, data type and base my $tIdx = 1; my $bIdx = 2; my %device = (); # Empty hash to record which rec types have device support @@ -58,6 +58,13 @@ my %fieldType = ( DBF_NOACCESS => "ezcaNoAccess" ); +# globals for parallel_caget +my %callback_data; +my $callback_incomplete; +# globals for caget +my $caget_data; +my $caget_incomplete; +my $cadebug = 0; ######### Main program ############ @@ -286,6 +293,49 @@ sub printField { return($col); } +# grab a list of pvs simultaneously +# The results are filled in the the %callback_data global hash +# and the result of the operation is the number of read pvs +# Usage: $read_pvs = parallel_caget( @pvlist ) +sub parallel_caget { + my @chans = map { CA->new($_); } @_; + my $wait = 1; + + eval { CA->pend_io($wait); }; + if ($@) { + if ($@ =~ m/^ECA_TIMEOUT/) { + my $err = (@chans > 1) ? 'some PV(s)' : "'" . $chans[0]->name . "'"; + print "Channel connect timed out: $err not found.\n"; + foreach my $chan (@chans) { + $callback_data{$chan->name} = "" + unless $chan->is_connected; + } + @chans = grep { $_->is_connected } @chans; + } else { + die $@; + } + } + + map { + my $type; + $type = $_->field_type; + #$callback_data{$_->name} = undef; + $_->get_callback(\&caget_callback, $type); + } @chans; + + my $read_pvs = @chans; + $callback_incomplete = @chans; + CA->pend_event(0.1) while $callback_incomplete; + return $read_pvs; +} + +sub caget_callback { + my ($chan, $status, $data) = @_; + die $status if $status; + $callback_data{$chan->name} = $data; + $callback_incomplete--; +} + # Given record name and interest level prints data from record fields # that are at or below the interest level specified. # Useage: printRecord( $recordName, $interestLevel) @@ -295,12 +345,16 @@ sub printRecord { my ($error, $recType, $field, $fType, $fInterest, $data); my ($fToGet, $col, $base); #print("checking record $name, interest $interest\n"); - $col = 0; $recType = getRecType($name); print("$name is record type $recType\n"); exists($record{$recType}) || die "Record type $recType not found in dbd file --"; - + + #capture list of fields to obtain + my @list = (); #fiels to read + my @fields_pr = (); #fields for print-out + my @ftypes = (); #types, from parser + my @bases = (); #bases, from parser foreach $field (sort keys %{$record{$recType}}) { # Skip DTYP field if this rec type doesn't have device support defined if($field eq "DTYP" && !(exists($device{$recType}))) { next; } @@ -309,15 +363,25 @@ sub printRecord { unless( $fType eq "ezcaNoAccess" ) { if( $interest >= $fInterest ) { $fToGet = $name . "." . $field; - $data = caget($fToGet); - chomp $data; - #$data =~ s/\s+//; - #if( $data[0] ) { die "ca get error on field $field --" }; - $col = printField($field, $data, $fType, $base, $col); - #print ("fType = $fType $data[1]\n"); + push @fields_pr, $field; + push @list, $fToGet; + push @ftypes, $fType; + push @bases, $base; } } } + my $read_pvs; + $read_pvs = parallel_caget( @list ); + + $col = 0; + for (my $i=0; $i < scalar @list; $i++) { + $field = $fields_pr[$i]; + $fToGet = $list[$i]; + $data = $callback_data{$fToGet}; + $fType = $ftypes[$i]; + chomp $data; + $col = printField($field, $data, $fType, $base, $col); + } print("\n"); # Final line feed } @@ -361,10 +425,6 @@ sub printRecordList { } } -# variable to communicate callback handler result -my $callback_data; -my $incomplete; -my $cadebug = 0; # returns a terse result of a caget operation # using perlCA @@ -385,9 +445,9 @@ sub caget { my $type = $channel->field_type; my $count = $channel->element_count; $channel->get_callback(\&callback_handler, $type); - $incomplete = 1; - CA->pend_event(0.1) while ($incomplete); - return $callback_data; + $caget_incomplete = 1; + CA->pend_event(0.1) while ($caget_incomplete); + return $caget_data; } sub callback_handler @@ -395,8 +455,8 @@ sub callback_handler my ($chan, $status, $data) = @_; die $status if $status; #display($chan, $type, $data); - $callback_data = $data; - $incomplete=0; + $caget_data = $data; + $caget_incomplete=0; } sub show_call_stack { From 18a63f8754367d66adb62aeeeec3668196b93d60 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 22:20:18 +0100 Subject: [PATCH 05/16] re-organizing print out and diagnostics --- src/cap5/capr.pl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 8986cf1f4..d2a5f0e35 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -265,11 +265,10 @@ sub printField { $outStr = sprintf("%-5s %x", $field, $fieldData); } elsif ( $dataType eq "ezcaDouble" || $dataType eq "ezcaFloat" ) { $outStr = sprintf("%-5s %.8f", $field, $fieldData); - } elsif ( $dataType eq "ezcaShort" ) { - $outStr = sprintf("%-5s %d", $field, $fieldData); } elsif ( $dataType eq "ezcaChar" ) { $outStr = sprintf("%-5s %d", $field, ord($fieldData)); }else { + # ezcaByte, ezcaShort, ezcaLong $outStr = sprintf("%-5s %d", $field, $fieldData); } @@ -280,7 +279,6 @@ sub printField { else { $wide = 80;} $pad = $wide - $len; - #print "outStr=$outStr .. pad=$pad\n"; if( $col + $wide > $screenWidth ) { print("\n"); @@ -373,6 +371,18 @@ sub printRecord { my $read_pvs; $read_pvs = parallel_caget( @list ); + print "====-------------===="; + for (my $i=0; $i < scalar @list; $i++) { + $field = $fields_pr[$i]; + $fToGet = $list[$i]; + $data = $callback_data{$fToGet}; + $fType = $ftypes[$i]; + $base = $bases[$i]; + my $len = length $data; + chomp $data; + print "$field ($fType-$len): $data\n"; + } + print "-------------===="; $col = 0; for (my $i=0; $i < scalar @list; $i++) { $field = $fields_pr[$i]; From c6255ef0ac187d1fccdad4e1872447e2f6899080 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 22:23:22 +0100 Subject: [PATCH 06/16] removing diagnostics --- src/cap5/capr.pl | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index d2a5f0e35..41b929908 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -368,21 +368,8 @@ sub printRecord { } } } - my $read_pvs; - $read_pvs = parallel_caget( @list ); + my $read_pvs = parallel_caget( @list ); - print "====-------------===="; - for (my $i=0; $i < scalar @list; $i++) { - $field = $fields_pr[$i]; - $fToGet = $list[$i]; - $data = $callback_data{$fToGet}; - $fType = $ftypes[$i]; - $base = $bases[$i]; - my $len = length $data; - chomp $data; - print "$field ($fType-$len): $data\n"; - } - print "-------------===="; $col = 0; for (my $i=0; $i < scalar @list; $i++) { $field = $fields_pr[$i]; From 5dc869bbcc2361d3fa055b9269c9ed88ae0c9188 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 22:44:58 +0100 Subject: [PATCH 07/16] Require default dbd --- src/cap5/capr.pl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 41b929908..23cfab8e2 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -17,10 +17,6 @@ use CA; ######### Globals ########## -# Non APS users will want to modify theDbdFile default settings - -my $theDbdFile = "/net/helios/iocapps/R3.13.9/ioc/linac/2/dbd/linac.dbd"; - my $hostArch; if ( defined $ENV{"EPICS_HOST_ARCH"} ) { $hostArch = $ENV{"EPICS_HOST_ARCH"} ; @@ -30,6 +26,7 @@ if ( defined $ENV{"EPICS_HOST_ARCH"} ) { our( $opt_d, $opt_f, $opt_r); my $usage = "Usage:\n $0 [-d dbd_file] ( [-f record_type] | [-r] | [record_name] ) [interest]\n"; +my $theDbdFile; my %record = (); # Empty hash to put dbd data in my $iIdx = 0; # Array indexes for interest, data type and base my $tIdx = 1; @@ -77,6 +74,11 @@ if($opt_d) { # command line has highest priority elsif (exists $ENV{CAPR_DBD_FILE}) { # Use the env var if it exists $theDbdFile = $ENV{CAPR_DBD_FILE}; } # Otherwise use the default set above +else { + die "Error: no default dbd defined\n". + "Specify dbd with -d option or CAPR_DBD_FILE environment variable\n". + $usage; +} parseDbd($theDbdFile); print "Using $theDbdFile\n\n"; From 30a58e4d4cf6f3083c99fa5486c47c25fd9dd801 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 22:59:17 +0100 Subject: [PATCH 08/16] Default interest level, clean up Removing one-parameter caget. Interest level zero if not specified --- src/cap5/capr.pl | 63 ++++++++++++++---------------------------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 23cfab8e2..10b2f3cec 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -55,12 +55,9 @@ my %fieldType = ( DBF_NOACCESS => "ezcaNoAccess" ); -# globals for parallel_caget +# globals for sub caget my %callback_data; my $callback_incomplete; -# globals for caget -my $caget_data; -my $caget_incomplete; my $cadebug = 0; ######### Main program ############ @@ -105,6 +102,7 @@ if($ARGV[0] =~ m/\s+\d/) { # If we replace comma with a space, } $ARGV[0] =~ s/\s+//; # Remove any spaces $ARGV[0] =~ s/\..*//; # Get rid of field name if it's there +$ARGV[1] = 0 unless defined $ARGV[1]; # default interest level is 0 $ARGV[1] =~ s/\D//g; # Make sure we only use digits $ARGV[1] = $ARGV[1] || 0; # interest defaults to 0 printRecord($ARGV[0], $ARGV[1]); # Do the do @@ -219,8 +217,10 @@ sub getRecType { my $type; my $data; - $data = caget($name); - if ($data =~ m/Invalid channel name/) { die "Record \"$_[0]\" not found\n"; } + my $read_pvs = caget( $name ); + + if ( $read_pvs != 1 ) { die "Record \"$_[0]\" not found\n"; } + $data = $callback_data{ $name }; chomp $data; $data =~ s/\s+//; #print("$name is a \"$data\"type\n"); @@ -293,14 +293,21 @@ sub printField { return($col); } -# grab a list of pvs simultaneously +# Query for a list of pvs simultaneously # The results are filled in the the %callback_data global hash # and the result of the operation is the number of read pvs -# Usage: $read_pvs = parallel_caget( @pvlist ) -sub parallel_caget { +# +# NOTE: Not re-entrant because results are written to global hash +# %callback_data +# +# Usage: $read_pvs = caget( @pvlist ) +sub caget { my @chans = map { CA->new($_); } @_; my $wait = 1; - + + #clear results; + %callback_data = (); + eval { CA->pend_io($wait); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { @@ -370,7 +377,7 @@ sub printRecord { } } } - my $read_pvs = parallel_caget( @list ); + my $read_pvs = caget( @list ); $col = 0; for (my $i=0; $i < scalar @list; $i++) { @@ -424,40 +431,6 @@ sub printRecordList { } } - -# returns a terse result of a caget operation -# using perlCA -sub caget { - my $name = $_[0]; - print $name . "\n" if $cadebug; - show_call_stack() if $cadebug; - my $channel = CA->new($name); - my $wait = 1; - eval { CA->pend_io($wait); }; - if ($@) { - if ($@ =~ m/^ECA_TIMEOUT/) { - my $err = "'" . $channel . "'"; - print "Channel connect timed out: $err not found.\n"; - } - die $@; - } - my $type = $channel->field_type; - my $count = $channel->element_count; - $channel->get_callback(\&callback_handler, $type); - $caget_incomplete = 1; - CA->pend_event(0.1) while ($caget_incomplete); - return $caget_data; -} - -sub callback_handler -{ - my ($chan, $status, $data) = @_; - die $status if $status; - #display($chan, $type, $data); - $caget_data = $data; - $caget_incomplete=0; -} - sub show_call_stack { my ( $path, $line, $subr ); my $max_depth = 30; From b45e6b818e54393806246e98b896f092e048e5b1 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Tue, 25 May 2010 23:13:47 +0100 Subject: [PATCH 09/16] renaming variables and cleaning-up --- src/cap5/capr.pl | 54 ++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 10b2f3cec..aa2fc0dce 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -217,9 +217,9 @@ sub getRecType { my $type; my $data; - my $read_pvs = caget( $name ); - - if ( $read_pvs != 1 ) { die "Record \"$_[0]\" not found\n"; } + my $fields_read = caget( $name ); + + if ( $fields_read != 1 ) { die "Record \"$_[0]\" not found\n"; } $data = $callback_data{ $name }; chomp $data; $data =~ s/\s+//; @@ -267,10 +267,10 @@ sub printField { $outStr = sprintf("%-5s %x", $field, $fieldData); } elsif ( $dataType eq "ezcaDouble" || $dataType eq "ezcaFloat" ) { $outStr = sprintf("%-5s %.8f", $field, $fieldData); - } elsif ( $dataType eq "ezcaChar" ) { + } elsif ( $dataType eq "ezcaChar" ) { $outStr = sprintf("%-5s %d", $field, ord($fieldData)); }else { - # ezcaByte, ezcaShort, ezcaLong + # ezcaByte, ezcaShort, ezcaLong $outStr = sprintf("%-5s %d", $field, $fieldData); } @@ -293,21 +293,21 @@ sub printField { return($col); } -# Query for a list of pvs simultaneously +# Query for a list of fields simultaneously. # The results are filled in the the %callback_data global hash # and the result of the operation is the number of read pvs # # NOTE: Not re-entrant because results are written to global hash # %callback_data # -# Usage: $read_pvs = caget( @pvlist ) +# Usage: $fields_read = caget( @pvlist ) sub caget { my @chans = map { CA->new($_); } @_; my $wait = 1; - + #clear results; %callback_data = (); - + eval { CA->pend_io($wait); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { @@ -330,10 +330,10 @@ sub caget { $_->get_callback(\&caget_callback, $type); } @chans; - my $read_pvs = @chans; + my $fields_read = @chans; $callback_incomplete = @chans; CA->pend_event(0.1) while $callback_incomplete; - return $read_pvs; + return $fields_read; } sub caget_callback { @@ -356,10 +356,10 @@ sub printRecord { $recType = getRecType($name); print("$name is record type $recType\n"); exists($record{$recType}) || die "Record type $recType not found in dbd file --"; - - #capture list of fields to obtain - my @list = (); #fiels to read - my @fields_pr = (); #fields for print-out + + #capture list of fields + my @readlist = (); #fields to read via CA + my @fields_pr = (); #fields for print-out my @ftypes = (); #types, from parser my @bases = (); #bases, from parser foreach $field (sort keys %{$record{$recType}}) { @@ -371,18 +371,19 @@ sub printRecord { if( $interest >= $fInterest ) { $fToGet = $name . "." . $field; push @fields_pr, $field; - push @list, $fToGet; + push @readlist, $fToGet; push @ftypes, $fType; push @bases, $base; } } } - my $read_pvs = caget( @list ); + my $fields_read = caget( @readlist ); + # print while iterating over lists gathered $col = 0; - for (my $i=0; $i < scalar @list; $i++) { + for (my $i=0; $i < scalar @readlist; $i++) { $field = $fields_pr[$i]; - $fToGet = $list[$i]; + $fToGet = $readlist[$i]; $data = $callback_data{$fToGet}; $fType = $ftypes[$i]; chomp $data; @@ -431,18 +432,3 @@ sub printRecordList { } } -sub show_call_stack { - my ( $path, $line, $subr ); - my $max_depth = 30; - my $i = 1; - print("--- Begin stack trace ---\n"); - my @call_details; - @call_details = caller($i); - while ( (@call_details) && ($i<$max_depth) ) { - print("$call_details[1] line $call_details[2] in function $call_details[3]\n"); - $i = $i +1; - @call_details = caller($i); - } - print("--- End stack trace ---\n"); -} - From ce4ba8bbaf5de2832600515b842bf5568501018b Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Wed, 26 May 2010 00:10:06 +0100 Subject: [PATCH 10/16] Help or capr.pl --- src/cap5/capr.pl | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index aa2fc0dce..c418a0f82 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -24,8 +24,8 @@ if ( defined $ENV{"EPICS_HOST_ARCH"} ) { $hostArch="solaris-sparc"; } -our( $opt_d, $opt_f, $opt_r); -my $usage = "Usage:\n $0 [-d dbd_file] ( [-f record_type] | [-r] | [record_name] ) [interest]\n"; +our( $opt_h, $opt_d, $opt_f, $opt_r); + my $theDbdFile; my %record = (); # Empty hash to put dbd data in my $iIdx = 0; # Array indexes for interest, data type and base @@ -62,7 +62,8 @@ my $cadebug = 0; ######### Main program ############ -getopts('d:f:r') or die "$usage"; +HELP_MESSAGE() unless getopts('hd:f:r'); +HELP_MESSAGE() if $opt_h; # Select the dbd file to use if($opt_d) { # command line has highest priority @@ -72,9 +73,7 @@ elsif (exists $ENV{CAPR_DBD_FILE}) { # Use the env var if it exists $theDbdFile = $ENV{CAPR_DBD_FILE}; } # Otherwise use the default set above else { - die "Error: no default dbd defined\n". - "Specify dbd with -d option or CAPR_DBD_FILE environment variable\n". - $usage; + die "No dbd file defined. ('capr.pl -h' gives help)\n"; } parseDbd($theDbdFile); @@ -95,7 +94,7 @@ if($opt_f) { # Do the business # Allow commas between arguments as in vxWorks dbpr -die "$usage" unless defined $ARGV[0]; +HELP_MESSAGE() unless defined $ARGV[0]; $ARGV[0] =~ s/,/ /; # Get rid of pesky comma if it's there if($ARGV[0] =~ m/\s+\d/) { # If we replace comma with a space, ($ARGV[0], $ARGV[1]) = split(/ /, $ARGV[0]); #split it @@ -345,7 +344,7 @@ sub caget_callback { # Given record name and interest level prints data from record fields # that are at or below the interest level specified. -# Useage: printRecord( $recordName, $interestLevel) +# Usage: printRecord( $recordName, $interestLevel) sub printRecord { my $name = $_[0]; my $interest = $_[1]; @@ -432,3 +431,25 @@ sub printRecordList { } } +sub HELP_MESSAGE { + print STDERR "\n", +"Usage: capr.pl -h\n", +" capr.pl [-d dbd_file] -r\n", +" capr.pl [-d dbd_file] -f \n", +" capr.pl [-d dbd_file] \n", +"Description:\n", +" Attempts to perform a record print \"dbpr\" via channel access\n", +" for record_name at a given interest level.\n", +" The default interest level is 0.\n\n", +" If used with the f or r options, prints fields/record type lists.\n", +"\n", +"Options:\n", +" -h: Help: Prints this message\n", +" -d Dbd file: specify dbd file used to read record definitions.\n", +" If omitted, the environment variable CAPR_DBD_FILE must be defined\n", +" -r Prints the list of record types\n", +" -f Prints list of fields, interest level, type and base for the\n", +" given record type\n", +"\n"; + exit 1; +} From 31931330a802acee1cecbec5c3e17170539e6859 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Wed, 26 May 2010 23:20:36 +0100 Subject: [PATCH 11/16] removing ezca strings mapping --- src/cap5/capr.pl | 98 +++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index c418a0f82..a445e55ad 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -17,13 +17,6 @@ use CA; ######### Globals ########## -my $hostArch; -if ( defined $ENV{"EPICS_HOST_ARCH"} ) { - $hostArch = $ENV{"EPICS_HOST_ARCH"} ; -} else { - $hostArch="solaris-sparc"; -} - our( $opt_h, $opt_d, $opt_f, $opt_r); my $theDbdFile; @@ -34,29 +27,29 @@ my $bIdx = 2; my %device = (); # Empty hash to record which rec types have device support my $DEBUG=0; # DEBUG -# EPICS field types referenced to their equivalent EZCA types +# EPICS field types my %fieldType = ( - DBF_STRING => "ezcaString", - DBF_BYTE => "ezcaByte", - DBF_CHAR => "ezcaByte", - DBF_UCHAR => "ezcaChar", - DBF_SHORT => "ezcaShort", - DBF_USHORT => "ezcaLong", - DBF_LONG => "ezcaLong", - DBF_ULONG => "ezcaDouble", - DBF_FLOAT => "ezcaFloat", - DBF_DOUBLE => "ezcaDouble", - DBF_ENUM => "ezcaString", - DBF_MENU => "ezcaString", - DBF_DEVICE => "ezcaString", - DBF_INLINK => "ezcaString", - DBF_OUTLINK => "ezcaString", - DBF_FWDLINK => "ezcaString", - DBF_NOACCESS => "ezcaNoAccess" + DBF_CHAR => "DBF_CHAR", + DBF_UCHAR => "DBF_CHAR", + DBF_DOUBLE => "DBF_FLOAT", + DBF_FLOAT => "DBF_FLOAT", + DBF_LONG => "DBF_LONG", + DBF_SHORT => "DBF_LONG", + DBF_ULONG => "DBF_LONG", + DBF_USHORT => "DBF_LONG", + DBF_DEVICE => "DBF_STRING", + DBF_ENUM => "DBF_STRING", + DBF_FWDLINK => "DBF_STRING", + DBF_INLINK => "DBF_STRING", + DBF_MENU => "DBF_STRING", + DBF_OUTLINK => "DBF_STRING", + DBF_STRING => "DBF_STRING", + DBF_NOACCESS => "DBF_NOACCESS", ); # globals for sub caget my %callback_data; +my %timed_out; my $callback_incomplete; my $cadebug = 0; @@ -69,9 +62,12 @@ HELP_MESSAGE() if $opt_h; if($opt_d) { # command line has highest priority $theDbdFile = $opt_d; } -elsif (exists $ENV{CAPR_DBD_FILE}) { # Use the env var if it exists - $theDbdFile = $ENV{CAPR_DBD_FILE}; +elsif (exists $ENV{EPICS_CAPR_DBD_FILE}) { # Use the env var if it exists + $theDbdFile = $ENV{EPICS_CAPR_DBD_FILE}; } # Otherwise use the default set above +elsif (exists $ENV{EPICS_BASE}) { + $theDbdFile = $ENV{EPICS_BASE} . "/dbd/softIoc.dbd"; +} else { die "No dbd file defined. ('capr.pl -h' gives help)\n"; } @@ -101,12 +97,15 @@ if($ARGV[0] =~ m/\s+\d/) { # If we replace comma with a space, } $ARGV[0] =~ s/\s+//; # Remove any spaces $ARGV[0] =~ s/\..*//; # Get rid of field name if it's there -$ARGV[1] = 0 unless defined $ARGV[1]; # default interest level is 0 -$ARGV[1] =~ s/\D//g; # Make sure we only use digits -$ARGV[1] = $ARGV[1] || 0; # interest defaults to 0 +if ( defined $ARGV[1] ) { + $ARGV[1] =~ s/\D//g; # Make sure we only use digits + $ARGV[1] = $ARGV[1] || 0; # blank => interest level set to 0 +} +else { + $ARGV[1] = 0; # interest defaults to 0 +} printRecord($ARGV[0], $ARGV[1]); # Do the do - ########## End of main ########### @@ -260,16 +259,17 @@ sub printField { $field = $fieldName . ":"; - if( $dataType eq "ezcaString" ) { + if( $dataType eq "DBF_STRING" ) { $outStr = sprintf("%-5s %s", $field, $fieldData); } elsif ( $base eq "HEX" ) { - $outStr = sprintf("%-5s %x", $field, $fieldData); - } elsif ( $dataType eq "ezcaDouble" || $dataType eq "ezcaFloat" ) { + my $val = ( $dataType eq "DBF_CHAR" ) ? ord($fieldData) : $fieldData; + $outStr = sprintf("%-5s %x", $field, $val); + } elsif ( $dataType eq "DBF_DOUBLE" || $dataType eq "DBF_FLOAT" ) { $outStr = sprintf("%-5s %.8f", $field, $fieldData); - } elsif ( $dataType eq "ezcaChar" ) { + } elsif ( $dataType eq "DBF_CHAR" ) { $outStr = sprintf("%-5s %d", $field, ord($fieldData)); }else { - # ezcaByte, ezcaShort, ezcaLong + # DBF_LONG, DBF_SHORT, DBF_UCHAR, DBF_ULONG, DBF_USHORT $outStr = sprintf("%-5s %d", $field, $fieldData); } @@ -306,6 +306,7 @@ sub caget { #clear results; %callback_data = (); + %timed_out = (); eval { CA->pend_io($wait); }; if ($@) { @@ -313,8 +314,7 @@ sub caget { my $err = (@chans > 1) ? 'some PV(s)' : "'" . $chans[0]->name . "'"; print "Channel connect timed out: $err not found.\n"; foreach my $chan (@chans) { - $callback_data{$chan->name} = "" - unless $chan->is_connected; + $timed_out{$chan->name} = ( $chan->is_connected ) ? 0 : 1; } @chans = grep { $_->is_connected } @chans; } else { @@ -366,7 +366,7 @@ sub printRecord { if($field eq "DTYP" && !(exists($device{$recType}))) { next; } ($fType, $fInterest, $base) = getFieldParams($recType, $field); - unless( $fType eq "ezcaNoAccess" ) { + unless( $fType eq "DBF_NOACCESS" ) { if( $interest >= $fInterest ) { $fToGet = $name . "." . $field; push @fields_pr, $field; @@ -383,9 +383,15 @@ sub printRecord { for (my $i=0; $i < scalar @readlist; $i++) { $field = $fields_pr[$i]; $fToGet = $readlist[$i]; - $data = $callback_data{$fToGet}; - $fType = $ftypes[$i]; - chomp $data; + if ( $timed_out{$fToGet} ) { + $fType = $fieldType{DBF_STRING}; + $data = ""; + } + else { + $fType = $ftypes[$i]; + $data = $callback_data{$fToGet}; + chomp $data; + } $col = printField($field, $data, $fType, $base, $col); } print("\n"); # Final line feed @@ -438,15 +444,15 @@ sub HELP_MESSAGE { " capr.pl [-d dbd_file] -f \n", " capr.pl [-d dbd_file] \n", "Description:\n", -" Attempts to perform a record print \"dbpr\" via channel access\n", -" for record_name at a given interest level.\n", -" The default interest level is 0.\n\n", +" Attempts to perform a 'dbpr' record print via channel access for record_name at a given\n", +" interest level. The default interest level is 0.\n\n", " If used with the f or r options, prints fields/record type lists.\n", "\n", "Options:\n", " -h: Help: Prints this message\n", " -d Dbd file: specify dbd file used to read record definitions.\n", -" If omitted, the environment variable CAPR_DBD_FILE must be defined\n", +" The default can be specified with the EPICS_CAPR_DBD_FILE environment\n", +" variable. The default file is \$(EPICS_BASE)/dbd/softIoc.dbd\n", " -r Prints the list of record types\n", " -f Prints list of fields, interest level, type and base for the\n", " given record type\n", From 07d51b65b249fd2c503643e315dd51508dae0149 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Wed, 26 May 2010 23:49:21 +0100 Subject: [PATCH 12/16] consistently use double quotes --- src/cap5/capr.pl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index a445e55ad..9519078e1 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -55,7 +55,7 @@ my $cadebug = 0; ######### Main program ############ -HELP_MESSAGE() unless getopts('hd:f:r'); +HELP_MESSAGE() unless getopts("hd:f:r"); HELP_MESSAGE() if $opt_h; # Select the dbd file to use @@ -69,7 +69,7 @@ elsif (exists $ENV{EPICS_BASE}) { $theDbdFile = $ENV{EPICS_BASE} . "/dbd/softIoc.dbd"; } else { - die "No dbd file defined. ('capr.pl -h' gives help)\n"; + die "No dbd file defined. (\"capr.pl -h\" gives help)\n"; } parseDbd($theDbdFile); @@ -217,7 +217,7 @@ sub getRecType { my $fields_read = caget( $name ); - if ( $fields_read != 1 ) { die "Record \"$_[0]\" not found\n"; } + if ( $fields_read != 1 ) { die "Could not determine \"$_[0]\" record type.\n"; } $data = $callback_data{ $name }; chomp $data; $data =~ s/\s+//; @@ -311,7 +311,7 @@ sub caget { eval { CA->pend_io($wait); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { - my $err = (@chans > 1) ? 'some PV(s)' : "'" . $chans[0]->name . "'"; + my $err = (@chans > 1) ? "some PV(s)" : "\"" . $chans[0]->name . "\""; print "Channel connect timed out: $err not found.\n"; foreach my $chan (@chans) { $timed_out{$chan->name} = ( $chan->is_connected ) ? 0 : 1; @@ -444,7 +444,7 @@ sub HELP_MESSAGE { " capr.pl [-d dbd_file] -f \n", " capr.pl [-d dbd_file] \n", "Description:\n", -" Attempts to perform a 'dbpr' record print via channel access for record_name at a given\n", +" Attempts to perform a \"dbpr\" record print via channel access for record_name at a given\n", " interest level. The default interest level is 0.\n\n", " If used with the f or r options, prints fields/record type lists.\n", "\n", From ba42c501df82a6333e4a7a38c5d0cb68ea93681e Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Thu, 27 May 2010 11:52:21 +0100 Subject: [PATCH 13/16] Simplistic solution to parser problems --- src/cap5/capr.pl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 9519078e1..692d89c1b 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -146,14 +146,14 @@ sub parseDbd { chomp; print("line $i - level $level\n") if ($DEBUG); #$line = $dbd[$i] || die "Unexpected end of file: $dbdFile, line $."; - if( m/recordtype/ ) { + if( m/recordtype\(/ ) { ($level == 0) || die "dbd file format error in or before line $i --"; m/\((.*)\)/; #get record type #@records = (@records, $1); $isArecord = 1; $thisRecord = $1; } - if( m/field/ ) { + if( m/field\(/ ) { ($level == 1 && $isArecord) || die "dbd file format error in or before line $i --"; m/\((.*),/; # get field name $thisField = $1; @@ -162,12 +162,12 @@ sub parseDbd { $isAfield = 1; #print("$1 , line $i "); } - if( m/interest/ ) { + if( m/interest\(/ ) { ($level == 2 && $isAfield) || die "dbd file format error in or before line $i --"; m/\((.*)\)/ ; # get interest level, default = 0 $interest = $1; } - if( m/base/ ) { + if( m/base\(/ ) { ($level == 2 && $isAfield) || die "dbd file format error in or before line $i --"; m/\((.*)\)/ ; # get base, default = DECIMAL $thisBase = $1; @@ -196,7 +196,7 @@ sub parseDbd { $level--; } # Parse for record types with device support - if( m/device/ ) { + if( m/device\(/ ) { m/\((.*?),/; if(!exists($device{$1})) { # Use a hash to make a list of record types with device support From 5ad3391be259b6d37678115ab119f3c06e810e43 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Thu, 27 May 2010 14:03:17 +0100 Subject: [PATCH 14/16] create test cases from I02 beamline --- src/cap5/test-cases.pl | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/cap5/test-cases.pl diff --git a/src/cap5/test-cases.pl b/src/cap5/test-cases.pl new file mode 100644 index 000000000..118f5d808 --- /dev/null +++ b/src/cap5/test-cases.pl @@ -0,0 +1,11 @@ +grep record BL02I-MO-IOC-02.db |\ +grep -v '^#' |\ +perl -e ' +%h = (); +while(<>){ + @a = split /,/; + $a[0] =~ s/record\(//; + $h{$a[0]}=$a[1]; +} +print %h;' + From a24f6371ac463db8faa8709cffa309dbdb50ee99 Mon Sep 17 00:00:00 2001 From: Ronaldo Mercado Date: Thu, 27 May 2010 16:24:09 +0100 Subject: [PATCH 15/16] Removes complaints when the string to print has dollars. --- src/cap5/capr.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 692d89c1b..84a843322 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -286,7 +286,7 @@ sub printField { $col = 0; } - print sprintf("$outStr%*s",$pad," "); + print $outStr . sprintf("%*s",$pad," "); $col = $col + $wide; return($col); From cfb8e694a5b935501cb38be0abc5c5407590a5c6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 2 Jul 2010 11:34:38 -0500 Subject: [PATCH 16/16] Several cleanups. * Fixed number base issue * DBD file parser accepts spaces * Added -w seconds option for CA timeout * Improved argument parsing * More perlish, should start up faster --- src/cap5/capr.pl | 422 +++++++++++++++++++++-------------------------- 1 file changed, 192 insertions(+), 230 deletions(-) diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl index 84a843322..4f8abac3d 100644 --- a/src/cap5/capr.pl +++ b/src/cap5/capr.pl @@ -13,283 +13,250 @@ use FindBin qw($Bin); use lib "$Bin/../../lib/perl"; use Getopt::Std; +use EPICS::Path; use CA; ######### Globals ########## -our( $opt_h, $opt_d, $opt_f, $opt_r); +our ($opt_h, $opt_f, $opt_r); +our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} || "$Bin/../../dbd/softIoc.dbd"; +our $opt_w = 1; -my $theDbdFile; my %record = (); # Empty hash to put dbd data in my $iIdx = 0; # Array indexes for interest, data type and base my $tIdx = 1; my $bIdx = 2; my %device = (); # Empty hash to record which rec types have device support -my $DEBUG=0; # DEBUG # EPICS field types my %fieldType = ( - DBF_CHAR => "DBF_CHAR", - DBF_UCHAR => "DBF_CHAR", - DBF_DOUBLE => "DBF_FLOAT", - DBF_FLOAT => "DBF_FLOAT", - DBF_LONG => "DBF_LONG", - DBF_SHORT => "DBF_LONG", - DBF_ULONG => "DBF_LONG", - DBF_USHORT => "DBF_LONG", - DBF_DEVICE => "DBF_STRING", - DBF_ENUM => "DBF_STRING", - DBF_FWDLINK => "DBF_STRING", - DBF_INLINK => "DBF_STRING", - DBF_MENU => "DBF_STRING", - DBF_OUTLINK => "DBF_STRING", - DBF_STRING => "DBF_STRING", - DBF_NOACCESS => "DBF_NOACCESS", + DBF_CHAR => 'DBF_CHAR', + DBF_UCHAR => 'DBF_CHAR', + DBF_DOUBLE => 'DBF_FLOAT', + DBF_FLOAT => 'DBF_FLOAT', + DBF_LONG => 'DBF_LONG', + DBF_SHORT => 'DBF_LONG', + DBF_ULONG => 'DBF_LONG', + DBF_USHORT => 'DBF_LONG', + DBF_DEVICE => 'DBF_STRING', + DBF_ENUM => 'DBF_STRING', + DBF_FWDLINK => 'DBF_STRING', + DBF_INLINK => 'DBF_STRING', + DBF_MENU => 'DBF_STRING', + DBF_OUTLINK => 'DBF_STRING', + DBF_STRING => 'DBF_STRING', + DBF_NOACCESS => 'DBF_NOACCESS', ); # globals for sub caget my %callback_data; my %timed_out; my $callback_incomplete; -my $cadebug = 0; ######### Main program ############ -HELP_MESSAGE() unless getopts("hd:f:r"); +HELP_MESSAGE() unless getopts('hd:f:rw:'); HELP_MESSAGE() if $opt_h; -# Select the dbd file to use -if($opt_d) { # command line has highest priority - $theDbdFile = $opt_d; -} -elsif (exists $ENV{EPICS_CAPR_DBD_FILE}) { # Use the env var if it exists - $theDbdFile = $ENV{EPICS_CAPR_DBD_FILE}; -} # Otherwise use the default set above -elsif (exists $ENV{EPICS_BASE}) { - $theDbdFile = $ENV{EPICS_BASE} . "/dbd/softIoc.dbd"; -} -else { - die "No dbd file defined. (\"capr.pl -h\" gives help)\n"; -} +die "File $opt_d not found. (\"capr.pl -h\" gives help)\n" + unless -f $opt_d; -parseDbd($theDbdFile); -print "Using $theDbdFile\n\n"; +parseDbd($opt_d); +print "Using $opt_d\n\n"; # Print a list of record types -if($opt_r) { - print ("Record types defined in $theDbdFile\n"); +if ($opt_r) { + print ("Record types found:\n"); printList(0); exit; } # Print the fields defined for given record -if($opt_f) { +if ($opt_f) { printRecordList($opt_f); exit; } -# Do the business -# Allow commas between arguments as in vxWorks dbpr -HELP_MESSAGE() unless defined $ARGV[0]; -$ARGV[0] =~ s/,/ /; # Get rid of pesky comma if it's there -if($ARGV[0] =~ m/\s+\d/) { # If we replace comma with a space, - ($ARGV[0], $ARGV[1]) = split(/ /, $ARGV[0]); #split it +HELP_MESSAGE() unless @ARGV; + +$_ = shift; +if (@ARGV) { + # Drop any ".FIELD" part + s/\. \w+ $//x; + printRecord($_, @ARGV); +} else { + if (m/^ \s* ([]+:;<>0-9A-Za-z[-]+) (?:\. \w+)? \s* , \s* (\d+) \s* $/x) { + # Recognizes ",n" as an interest leve, drops any ".FIELD" part + printRecord($1, $2); + } else { + # Drop any ".FIELD" part + s/\. \w+ $//x; + printRecord($_, 0); + } } -$ARGV[0] =~ s/\s+//; # Remove any spaces -$ARGV[0] =~ s/\..*//; # Get rid of field name if it's there -if ( defined $ARGV[1] ) { - $ARGV[1] =~ s/\D//g; # Make sure we only use digits - $ARGV[1] = $ARGV[1] || 0; # blank => interest level set to 0 -} -else { - $ARGV[1] = 0; # interest defaults to 0 -} -printRecord($ARGV[0], $ARGV[1]); # Do the do ########## End of main ########### # parseDbd -# takes given dbd file and parses it to produce a hash table of record types -# giving their fields, and for each field its interest level and data type -# usage: void parseDbd("fileName"); -# Output is in the hash %record. This is a hash of (references to) another. -# hash containing the fields of this record, as keys. The value keyed by -# the field names are (references to) arrays. Each of these arrays contains -# the interest level, data type and base of the field +# Takes given dbd file and parses it to produce a hash table of record types +# giving their fields, and for each field its interest level and data type. +# usage: parseDbd("fileName"); +# Output goes to the global %record, a hash of references to other hashes +# containing the fields of each record type. Those hash values (keyed by +# field name) are references to arrays containing the interest level, data +# type and number base of the field. sub parseDbd { - my $dbdFile = $_[0]; - my @dbd; - my $length; + my $dbdFile = shift; + + open(DBD, "< $dbdFile") or die "Can't open file $dbdFile: $!\n"; + my @dbd = ; + close(DBD) or die "Can't close $dbdFile: $!\n"; + + my $i = 1; my $level = 0; - my $i = 0; my $isArecord = 0; - my $isAfield; + my $isAfield = 0; my $thisRecord; my $thisField; my $thisType; - my %field = (); - my @params = (); + my $field = {}; my $interest = 0; - my $thisBase = "DECIMAL"; - my $item; - my $newDevice; + my $thisBase = 'DECIMAL'; - open(DBD, "< $dbdFile") || die "Can't open dbd file $dbdFile --"; - @dbd = ; - $length = @dbd; - close(DBD) || die "Can't close $dbdFile --"; - - while ($i < $length) { - $_ = $dbd[$i]; + while (@dbd) { + $_ = shift @dbd; chomp; - print("line $i - level $level\n") if ($DEBUG); - #$line = $dbd[$i] || die "Unexpected end of file: $dbdFile, line $."; - if( m/recordtype\(/ ) { - ($level == 0) || die "dbd file format error in or before line $i --"; - m/\((.*)\)/; #get record type - #@records = (@records, $1); + if ( m/recordtype \s* \( \s* (\w+) \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 0; $isArecord = 1; $thisRecord = $1; } - if( m/field\(/ ) { - ($level == 1 && $isArecord) || die "dbd file format error in or before line $i --"; - m/\((.*),/; # get field name + elsif ( m/field \s* \( \s* (\w+) \s* , \s* (\w+) \s* \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 1 && $isArecord; $thisField = $1; - m/,(.*)\)/; # get field type - $thisType = $1; + $thisType = $2; $isAfield = 1; - #print("$1 , line $i "); } - if( m/interest\(/ ) { - ($level == 2 && $isAfield) || die "dbd file format error in or before line $i --"; - m/\((.*)\)/ ; # get interest level, default = 0 + elsif ( m/interest \s* \( \s* (\w+) \s* \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 2 && $isAfield; $interest = $1; } - if( m/base\(/ ) { - ($level == 2 && $isAfield) || die "dbd file format error in or before line $i --"; - m/\((.*)\)/ ; # get base, default = DECIMAL + elsif ( m/base \s* \( \s* (\w+) \s* \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 2 && $isAfield; $thisBase = $1; } - if( m/\{/ ) { $level++ }; - if( m/\}/ ) { - if( $level == 2 && $isAfield) { + elsif ( m/device \s* \( (\w+) \s* ,/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 0; + $device{$1}++; + } + if ( m/\{/ ) { + $level++; + } + if ( m/\}/ ) { + if ($level == 2 && $isAfield) { + my $params = []; + $params->[$iIdx] = $interest; + $params->[$tIdx] = $thisType; + $params->[$bIdx] = $thisBase; + $field->{$thisField} = $params; $isAfield = 0; - $params[$iIdx] = $interest; - $params[$tIdx] = $thisType; - $params[$bIdx] = $thisBase; - $field{$thisField} = [@params]; - #print("interest $interest\n"); - $interest = 0; # set up default for next time - $thisBase = "DECIMAL"; # set up default for next time + $interest = 0; # reset default + $thisBase = 'DECIMAL'; # reset default } - if( $level == 1 && $isArecord) { + elsif ($level == 1 && $isArecord) { $isArecord = 0; - $record{$thisRecord} = { %field }; - #print("record type $thisRecord "); - #foreach $key (keys(%field)) { - # print("Field $key - interest $field{$key}\n"); - #} - %field = (); # set up for next time + $record{$thisRecord} = $field; + $field = {}; # start another hash } $level--; } - # Parse for record types with device support - if( m/device\(/ ) { - m/\((.*?),/; - if(!exists($device{$1})) { - # Use a hash to make a list of record types with device support - $device{$1} = 1; - } - } $i++; } } -# Given a record name attempts to find the record and its type. -# Usage: getRecType(recordName) - returns ($error, $recordType) +# Given a record name, attempts to find the record and its type. +# Usage: $recordType = getRecType("recordName"); sub getRecType { - my $name = $_[0] . ".RTYP"; - my $type; - my $data; + my $arg = shift; + my $name = "$arg.RTYP"; - my $fields_read = caget( $name ); + my $fields_read = caget($name); - if ( $fields_read != 1 ) { die "Could not determine \"$_[0]\" record type.\n"; } - $data = $callback_data{ $name }; - chomp $data; - $data =~ s/\s+//; - #print("$name is a \"$data\"type\n"); - return($data); + die "Could not determine record type of $arg\n" + unless $fields_read == 1; + + return $callback_data{$name}; } -# Given the record type and the field returns the interest level, data type -# and base for the field -# Usage: ($dataType, $interest, $base) getFieldParams( $recType, $field) +# Given the record type and field, returns the interest level, data type +# and number base for the field +# Usage: ($dataType, $interest, $base) = getFieldParams($recType, $field); sub getFieldParams { - my $recType = $_[0]; - my $field = $_[1]; - my ($fType, $fInterest, $fBase); + my ($recType, $field) = @_; - exists($fieldType{$record{$recType}{$field}[$tIdx]}) || + my $params = $record{$recType}{$field} or + die "Can't find params for $recType.$field"; + exists($fieldType{$params->[$tIdx]}) || die "Field data type $field for $recType not found in dbd file --"; - exists($record{$recType}{$field}[$iIdx]) || + exists($params->[$iIdx]) || die "Interest level for $field in $recType not found in dbd file --"; - $fType = $fieldType{$record{$recType}{$field}[$tIdx]}; - $fInterest = $record{$recType}{$field}[$iIdx]; - $fBase = $record{$recType}{$field}[$bIdx]; - return($fType, $fInterest, $fBase); + my $fType = $fieldType{$params->[$tIdx]}; + my $fInterest = $params->[$iIdx]; + my $fBase = $params->[$bIdx]; + return ($fType, $fInterest, $fBase); } # Prints field name and data for given field. Formats output so # that fields align in to 4 columns. Tries to imitate dbpf format # Usage: printField( $fieldName, $data, $dataType, $base, $firstColumnPosn) sub printField { - my $fieldName = $_[0]; - my $fieldData = $_[1]; - my $dataType = $_[2]; - my $base = $_[3]; # base to display numeric data in - my $col = $_[4]; # first column to print in + my ($fieldName, $fieldData, $dataType, $base, $col) = @_; my $screenWidth = 80; - my ($outStr, $len, $wide, $pad, $field); + my ($outStr, $wide); - $field = $fieldName . ":"; + my $field = "$fieldName:"; - if( $dataType eq "DBF_STRING" ) { - $outStr = sprintf("%-5s %s", $field, $fieldData); - } elsif ( $base eq "HEX" ) { - my $val = ( $dataType eq "DBF_CHAR" ) ? ord($fieldData) : $fieldData; - $outStr = sprintf("%-5s %x", $field, $val); - } elsif ( $dataType eq "DBF_DOUBLE" || $dataType eq "DBF_FLOAT" ) { - $outStr = sprintf("%-5s %.8f", $field, $fieldData); - } elsif ( $dataType eq "DBF_CHAR" ) { - $outStr = sprintf("%-5s %d", $field, ord($fieldData)); + if ( $dataType eq 'DBF_STRING' ) { + $outStr = sprintf('%-5s %s', $field, $fieldData); + } elsif ( $base eq 'HEX' ) { + my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData; + $outStr = sprintf('%-5s 0x%x', $field, $val); + } elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) { + $outStr = sprintf('%-5s %.8f', $field, $fieldData); + } elsif ( $dataType eq 'DBF_CHAR' ) { + $outStr = sprintf('%-5s %d', $field, ord($fieldData)); }else { # DBF_LONG, DBF_SHORT, DBF_UCHAR, DBF_ULONG, DBF_USHORT - $outStr = sprintf("%-5s %d", $field, $fieldData); + $outStr = sprintf('%-5s %d', $field, $fieldData); } - $len = length($outStr); - if($len <= 20) { $wide = 20; } - elsif( $len <= 40 ) { $wide = 40; } - elsif( $len <= 60 ) { $wide = 60; } + my $len = length($outStr); + if ($len <= 20) { $wide = 20; } + elsif ( $len <= 40 ) { $wide = 40; } + elsif ( $len <= 60 ) { $wide = 60; } else { $wide = 80;} - $pad = $wide - $len; + my $pad = $wide - $len; - if( $col + $wide > $screenWidth ) { + $col += $wide; + if ($col > $screenWidth ) { print("\n"); - $col = 0; + $col = $wide; } - print $outStr . sprintf("%*s",$pad," "); - $col = $col + $wide; + print $outStr, ' ' x $pad; - return($col); + return $col; } # Query for a list of fields simultaneously. @@ -302,19 +269,18 @@ sub printField { # Usage: $fields_read = caget( @pvlist ) sub caget { my @chans = map { CA->new($_); } @_; - my $wait = 1; #clear results; %callback_data = (); %timed_out = (); - eval { CA->pend_io($wait); }; + eval { CA->pend_io($opt_w); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { - my $err = (@chans > 1) ? "some PV(s)" : "\"" . $chans[0]->name . "\""; + my $err = (@chans > 1) ? 'some PV(s)' : '"' . $chans[0]->name . '"'; print "Channel connect timed out: $err not found.\n"; foreach my $chan (@chans) { - $timed_out{$chan->name} = ( $chan->is_connected ) ? 0 : 1; + $timed_out{$chan->name} = $chan->is_connected; } @chans = grep { $_->is_connected } @chans; } else { @@ -325,7 +291,6 @@ sub caget { map { my $type; $type = $_->field_type; - #$callback_data{$_->name} = undef; $_->get_callback(\&caget_callback, $type); } @chans; @@ -344,31 +309,28 @@ sub caget_callback { # Given record name and interest level prints data from record fields # that are at or below the interest level specified. -# Usage: printRecord( $recordName, $interestLevel) +# Usage: printRecord($recordName, $interestLevel) sub printRecord { - my $name = $_[0]; - my $interest = $_[1]; - my ($error, $recType, $field, $fType, $fInterest, $data); - my ($fToGet, $col, $base); - #print("checking record $name, interest $interest\n"); + my ($name, $interest) = @_; - $recType = getRecType($name); - print("$name is record type $recType\n"); - exists($record{$recType}) || die "Record type $recType not found in dbd file --"; + my $recType = getRecType($name); + print("Record $name type $recType\n"); + die "Record type $recType not found\n" + unless exists $record{$recType}; #capture list of fields my @readlist = (); #fields to read via CA my @fields_pr = (); #fields for print-out my @ftypes = (); #types, from parser my @bases = (); #bases, from parser - foreach $field (sort keys %{$record{$recType}}) { + foreach my $field (sort keys %{$record{$recType}}) { # Skip DTYP field if this rec type doesn't have device support defined - if($field eq "DTYP" && !(exists($device{$recType}))) { next; } + if ($field eq 'DTYP' && !(exists($device{$recType}))) { next; } - ($fType, $fInterest, $base) = getFieldParams($recType, $field); - unless( $fType eq "DBF_NOACCESS" ) { - if( $interest >= $fInterest ) { - $fToGet = $name . "." . $field; + my ($fType, $fInterest, $base) = getFieldParams($recType, $field); + unless( $fType eq 'DBF_NOACCESS' ) { + if ($interest >= $fInterest ) { + my $fToGet = "$name.$field"; push @fields_pr, $field; push @readlist, $fToGet; push @ftypes, $fType; @@ -379,22 +341,23 @@ sub printRecord { my $fields_read = caget( @readlist ); # print while iterating over lists gathered - $col = 0; + my $col = 0; for (my $i=0; $i < scalar @readlist; $i++) { - $field = $fields_pr[$i]; - $fToGet = $readlist[$i]; - if ( $timed_out{$fToGet} ) { + my $field = $fields_pr[$i]; + my $fToGet = $readlist[$i]; + my ($fType, $data, $base); + if ($timed_out{$fToGet}) { $fType = $fieldType{DBF_STRING}; - $data = ""; + $data = ''; } else { $fType = $ftypes[$i]; + $base = $bases[$i]; $data = $callback_data{$fToGet}; - chomp $data; } $col = printField($field, $data, $fType, $base, $col); } - print("\n"); # Final line feed + print("\n"); # Final newline } # Prints list of record types found in dbd file. If level > 0 @@ -402,13 +365,12 @@ sub printRecord { # also printed. # Diagnostic routine, usage: void printList(level); sub printList { - my $level = $_[0]; - my ($rkey, $fkey); + my $level = shift; - foreach $rkey (sort keys(%record)) { - print("$rkey\n"); - if($level > 0) { - foreach $fkey (keys %{$record{$rkey}}) { + foreach my $rkey (sort keys(%record)) { + print(" $rkey\n"); + if ($level > 0) { + foreach my $fkey (keys %{$record{$rkey}}) { print("\tField $fkey - interest $record{$rkey}{$fkey}[$iIdx] "); print("- type $record{$rkey}{$fkey}[$tIdx] "); print("- base $record{$rkey}{$fkey}[$bIdx]\n"); @@ -420,42 +382,42 @@ sub printList { # Prints list of fields with interest levels for given record type # Diagnostic routine, usage: void printRecordList("recordType"); sub printRecordList { - my ($rkey, $fkey); - my $type = $_[0]; + my $type = shift; - if( exists($record{$type}) ) { + if (exists($record{$type}) ) { print("Record type - $type\n"); - foreach $fkey (sort keys %{$record{$type}}) { - printf("%-4s", $fkey); + foreach my $fkey (sort keys %{$record{$type}}) { + printf('%-4s', $fkey); printf(" interest = $record{$type}{$fkey}[$iIdx]"); printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]); print (" base = $record{$type}{$fkey}[$bIdx]\n"); } } else { - print("Record type $type not defined in dbd file $theDbdFile\n"); + print("Record type $type not defined in dbd file $opt_d\n"); } } sub HELP_MESSAGE { print STDERR "\n", -"Usage: capr.pl -h\n", -" capr.pl [-d dbd_file] -r\n", -" capr.pl [-d dbd_file] -f \n", -" capr.pl [-d dbd_file] \n", -"Description:\n", -" Attempts to perform a \"dbpr\" record print via channel access for record_name at a given\n", -" interest level. The default interest level is 0.\n\n", -" If used with the f or r options, prints fields/record type lists.\n", -"\n", -"Options:\n", -" -h: Help: Prints this message\n", -" -d Dbd file: specify dbd file used to read record definitions.\n", -" The default can be specified with the EPICS_CAPR_DBD_FILE environment\n", -" variable. The default file is \$(EPICS_BASE)/dbd/softIoc.dbd\n", -" -r Prints the list of record types\n", -" -f Prints list of fields, interest level, type and base for the\n", -" given record type\n", -"\n"; + "Usage: capr.pl -h\n", + " capr.pl [-d file.dbd] [-w seconds] -r\n", + " capr.pl [-d file.dbd] [-w seconds] -f record_type\n", + " capr.pl [-d file.dbd] [-w seconds] record_name [interest]\n", + "Description:\n", + " Attempts to perform a \"dbpr\" record print via channel access for \n", + " record_name at an interest level which defaults to level 0.\n\n", + " The -r or -f options cause it to print record type or field lists.\n", + "\n", + "Options:\n", + " -h Prints this help message.\n", + " -r Lists all record types in the dbd file.\n", + " -f record_type: Lists all fields plus their interest level, data type\n", + " and number base for the given record_type.\n", + " -d file.dbd: The dbd file containing record type definitions.\n", + " This can be set using the EPICS_CAPR_DBD_FILE environment variable.\n", + " Currently ", AbsPath($opt_d), "\n", + " -w seconds: CA connection timeout, currently $opt_w\n", + "\n"; exit 1; }